mirror of
https://github.com/pissang/claygl.git
synced 2026-02-01 17:27:08 +00:00
support 3d texture
This commit is contained in:
parent
5b08297737
commit
56a9a407ad
@ -18,6 +18,7 @@ export type UniformType =
|
||||
| 'bool'
|
||||
| 'int'
|
||||
| 'sampler2D'
|
||||
| 'sampler2DArray'
|
||||
| 'samplerCube'
|
||||
| 'float'
|
||||
| 'vec2'
|
||||
@ -720,8 +721,8 @@ export class Shader<
|
||||
static Fragment = FragmentShader;
|
||||
}
|
||||
|
||||
export function isTextureUniform(uniform: { type: UniformType | Record<string, UniformType> }) {
|
||||
return uniform.type === 'sampler2D' || uniform.type === 'samplerCube';
|
||||
export function isTextureUniform(uniform: { type: UniformType }) {
|
||||
return uniform.type.startsWith('sampler');
|
||||
}
|
||||
|
||||
export default Shader;
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
import Texture, { TextureImageSource, TextureOpts, TexturePixelSource } from './Texture';
|
||||
import * as constants from './core/constants';
|
||||
import vendor from './core/vendor';
|
||||
import Renderer from './Renderer';
|
||||
|
||||
export interface Texture2DData {
|
||||
image?: TextureImageSource;
|
||||
@ -23,7 +21,6 @@ export interface Texture2DOpts extends TextureOpts, Texture2DData {
|
||||
* }, ....]
|
||||
*/
|
||||
mipmaps?: Texture2DData[];
|
||||
convertToPOT?: boolean;
|
||||
}
|
||||
/**
|
||||
* @example
|
||||
@ -104,25 +101,20 @@ class Texture2D extends Texture {
|
||||
}
|
||||
|
||||
load(src: string, crossOrigin?: string) {
|
||||
const image = vendor.createImage();
|
||||
if (crossOrigin) {
|
||||
image.crossOrigin = crossOrigin;
|
||||
}
|
||||
image.onload = () => {
|
||||
this.dirty();
|
||||
this.trigger('load', this);
|
||||
};
|
||||
image.onerror = () => {
|
||||
this.trigger('error', this);
|
||||
};
|
||||
|
||||
image.src = src;
|
||||
this.image = image;
|
||||
this.image = vendor.loadImage(
|
||||
src,
|
||||
crossOrigin,
|
||||
() => {
|
||||
this.dirty();
|
||||
this.trigger('load', this);
|
||||
},
|
||||
() => {
|
||||
this.trigger('error', this);
|
||||
}
|
||||
);
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
Texture2D.prototype.convertToPOT = false;
|
||||
|
||||
export default Texture2D;
|
||||
|
||||
111
src/Texture2DArray.ts
Normal file
111
src/Texture2DArray.ts
Normal file
@ -0,0 +1,111 @@
|
||||
import Texture, { TextureImageSource, TextureOpts, TexturePixelSource } from './Texture';
|
||||
import vendor from './core/vendor';
|
||||
|
||||
export type Texture2DArrayData = {
|
||||
image?: TextureImageSource[];
|
||||
pixels?: TexturePixelSource[];
|
||||
};
|
||||
|
||||
export interface Texture2DArrayOpts extends TextureOpts {
|
||||
/**
|
||||
* @example
|
||||
* [{
|
||||
* image: mipmap0,
|
||||
* pixels: null
|
||||
* }, {
|
||||
* image: mipmap1,
|
||||
* pixels: null
|
||||
* }, ....]
|
||||
*/
|
||||
mipmaps?: Texture2DArrayData[];
|
||||
}
|
||||
|
||||
interface Texture2DArray extends Omit<Texture2DArrayOpts, 'image'> {}
|
||||
class Texture2DArray extends Texture {
|
||||
readonly textureType = 'texture2DArray';
|
||||
|
||||
private _image: TextureImageSource[] = [];
|
||||
|
||||
constructor(opts?: Partial<Texture2DArrayOpts>) {
|
||||
super(opts);
|
||||
}
|
||||
|
||||
hasImage() {
|
||||
return this._image.length > 0;
|
||||
}
|
||||
|
||||
get image(): TextureImageSource[] {
|
||||
return this._image;
|
||||
}
|
||||
set image(val: TextureImageSource[]) {
|
||||
if (this._image !== val) {
|
||||
this._image = val;
|
||||
this.dirty();
|
||||
}
|
||||
}
|
||||
get width() {
|
||||
if (this.hasImage()) {
|
||||
return this.image[0].width;
|
||||
}
|
||||
return this._width;
|
||||
}
|
||||
set width(value: number) {
|
||||
if (this.hasImage()) {
|
||||
console.warn("Texture from image can't set width");
|
||||
} else {
|
||||
if (this._width !== value) {
|
||||
this.dirty();
|
||||
}
|
||||
this._width = value;
|
||||
}
|
||||
}
|
||||
get height() {
|
||||
if (this.hasImage()) {
|
||||
return this.image[0].height;
|
||||
}
|
||||
return this._height;
|
||||
}
|
||||
set height(value: number) {
|
||||
if (this.hasImage()) {
|
||||
console.warn("Texture from image can't set height");
|
||||
} else {
|
||||
if (this._height !== value) {
|
||||
this.dirty();
|
||||
}
|
||||
this._height = value;
|
||||
}
|
||||
}
|
||||
|
||||
isRenderable() {
|
||||
if (this.hasImage()) {
|
||||
return this.image[0].width > 0 && this.image[0].height > 0;
|
||||
} else {
|
||||
return !!(this.width && this.height);
|
||||
}
|
||||
}
|
||||
|
||||
load(srcList: string[], crossOrigin?: string) {
|
||||
let loading = 0;
|
||||
this.image = [];
|
||||
srcList.forEach((src, idx) => {
|
||||
this.image![idx] = vendor.loadImage(
|
||||
src,
|
||||
crossOrigin,
|
||||
() => {
|
||||
if (--loading === 0) {
|
||||
this.dirty();
|
||||
this.trigger('success', this);
|
||||
}
|
||||
},
|
||||
() => {
|
||||
loading--;
|
||||
}
|
||||
);
|
||||
loading++;
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export default Texture2DArray;
|
||||
48
src/Texture3D.ts
Normal file
48
src/Texture3D.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import Texture, { TextureOpts, TexturePixelSource } from './Texture';
|
||||
|
||||
export interface Texture3DData {
|
||||
/**
|
||||
* Pixels data. Will be ignored if image is set.
|
||||
*/
|
||||
pixels?: TexturePixelSource;
|
||||
}
|
||||
|
||||
export interface Texture3DOpts extends TextureOpts, Texture3DData {}
|
||||
|
||||
interface Texture3D extends Texture3DOpts {}
|
||||
class Texture3D extends Texture {
|
||||
readonly textureType = 'texture3D';
|
||||
|
||||
private _depth: number = 512;
|
||||
|
||||
pixels?: TexturePixelSource;
|
||||
|
||||
constructor(opts?: TextureOpts) {
|
||||
super(opts);
|
||||
}
|
||||
|
||||
get width() {
|
||||
return this._width;
|
||||
}
|
||||
set width(value: number) {
|
||||
this._width = value;
|
||||
}
|
||||
get height() {
|
||||
return this._height;
|
||||
}
|
||||
set height(value: number) {
|
||||
this._height = value;
|
||||
}
|
||||
get depth() {
|
||||
return this._depth;
|
||||
}
|
||||
set depth(value: number) {
|
||||
this._depth = value;
|
||||
}
|
||||
|
||||
isRenderable() {
|
||||
return this.width > 0 && this.height > 0 && this.depth > 0;
|
||||
}
|
||||
}
|
||||
|
||||
export default Texture3D;
|
||||
@ -1,9 +1,6 @@
|
||||
import Texture, { TextureImageSource, TextureOpts, TexturePixelSource } from './Texture';
|
||||
import * as constants from './core/constants';
|
||||
import * as mathUtil from './math/util';
|
||||
import vendor from './core/vendor';
|
||||
import Renderer from './Renderer';
|
||||
import { GLEnum } from './core/type';
|
||||
import { keys } from './core/util';
|
||||
|
||||
const isPowerOfTwo = mathUtil.isPowerOfTwo;
|
||||
@ -125,25 +122,20 @@ class TextureCube extends Texture {
|
||||
let loading = 0;
|
||||
this.image = {} as Record<CubeTarget, TextureImageSource>;
|
||||
(keys(imageList) as CubeTarget[]).forEach((target) => {
|
||||
const src = imageList[target];
|
||||
const image = vendor.createImage();
|
||||
if (crossOrigin) {
|
||||
image.crossOrigin = crossOrigin;
|
||||
}
|
||||
image.onload = () => {
|
||||
loading--;
|
||||
if (loading === 0) {
|
||||
this.dirty();
|
||||
this.trigger('success', this);
|
||||
this.image![target] = vendor.loadImage(
|
||||
imageList[target],
|
||||
crossOrigin,
|
||||
() => {
|
||||
if (--loading === 0) {
|
||||
this.dirty();
|
||||
this.trigger('success', this);
|
||||
}
|
||||
},
|
||||
() => {
|
||||
loading--;
|
||||
}
|
||||
};
|
||||
image.onerror = function () {
|
||||
loading--;
|
||||
};
|
||||
|
||||
);
|
||||
loading++;
|
||||
image.src = src;
|
||||
this.image![target] = image;
|
||||
});
|
||||
|
||||
return this;
|
||||
|
||||
@ -8,6 +8,12 @@ interface Vendor {
|
||||
createCanvas: () => HTMLCanvasElement;
|
||||
createBlankCanvas: (color: string) => HTMLCanvasElement;
|
||||
createImage: () => HTMLImageElement;
|
||||
loadImage: (
|
||||
src: string,
|
||||
crossOrigin?: string,
|
||||
onload?: () => void,
|
||||
onerror?: () => void
|
||||
) => HTMLImageElement;
|
||||
request: {
|
||||
get: typeof get;
|
||||
};
|
||||
@ -68,6 +74,21 @@ vendor.createImage = function () {
|
||||
return new g.Image();
|
||||
};
|
||||
|
||||
vendor.loadImage = function (src, crossOrigin, onload, onerror) {
|
||||
const image = vendor.createImage();
|
||||
if (crossOrigin) {
|
||||
image.crossOrigin = crossOrigin;
|
||||
}
|
||||
image.onload = function () {
|
||||
onload && onload();
|
||||
};
|
||||
image.onerror = function () {
|
||||
onerror && onerror();
|
||||
};
|
||||
image.src = src;
|
||||
return image;
|
||||
};
|
||||
|
||||
vendor.request = {
|
||||
get
|
||||
};
|
||||
|
||||
@ -2,10 +2,21 @@ import * as constants from '../core/constants';
|
||||
import { GLEnum } from '../core/type';
|
||||
import { getPossiblelInternalFormat } from '../Texture';
|
||||
import Texture2D, { Texture2DData } from '../Texture2D';
|
||||
import Texture2DArray, { Texture2DArrayData } from '../Texture2DArray';
|
||||
import Texture3D, { Texture3DData } from '../Texture3D';
|
||||
import TextureCube, { cubeTargets, TextureCubeData } from '../TextureCube';
|
||||
import GLExtension from './GLExtension';
|
||||
|
||||
function getAvailableMinFilter(texture: Texture2D | TextureCube) {
|
||||
type AllTextureType = Texture2D | TextureCube | Texture2DArray | Texture3D;
|
||||
|
||||
const textureTargetMap = {
|
||||
texture2D: constants.TEXTURE_2D,
|
||||
textureCube: constants.TEXTURE_CUBE_MAP,
|
||||
texture2DArray: constants.TEXTURE_2D_ARRAY,
|
||||
texture3D: constants.TEXTURE_3D
|
||||
};
|
||||
|
||||
function getAvailableMinFilter(texture: AllTextureType) {
|
||||
const minFilter = texture.minFilter;
|
||||
if (!texture.useMipmap) {
|
||||
return minFilter === constants.NEAREST_MIPMAP_NEAREST
|
||||
@ -23,14 +34,14 @@ class GLTexture {
|
||||
*/
|
||||
slot: number = -1;
|
||||
|
||||
private _texture: Texture2D | TextureCube;
|
||||
private _texture: AllTextureType;
|
||||
|
||||
/**
|
||||
* Instance of webgl texture
|
||||
*/
|
||||
private _webglIns?: WebGLTexture;
|
||||
|
||||
constructor(texture: Texture2D | TextureCube) {
|
||||
constructor(texture: AllTextureType) {
|
||||
this._texture = texture;
|
||||
}
|
||||
|
||||
@ -48,8 +59,7 @@ class GLTexture {
|
||||
|
||||
update(gl: WebGL2RenderingContext, glExt: GLExtension) {
|
||||
const texture = this._texture;
|
||||
const isTexture2D = texture.textureType === 'texture2D';
|
||||
const textureTarget = isTexture2D ? constants.TEXTURE_2D : constants.TEXTURE_CUBE_MAP;
|
||||
const textureTarget = textureTargetMap[texture.textureType];
|
||||
this.bind(gl);
|
||||
|
||||
// Pixel storage
|
||||
@ -63,7 +73,7 @@ class GLTexture {
|
||||
|
||||
const glInternalFormat =
|
||||
texture.internalFormat || getPossiblelInternalFormat(texture.format, texture.type);
|
||||
const mipmaps = texture.mipmaps || [];
|
||||
const mipmaps = (texture as Texture2D).mipmaps || [];
|
||||
const mipmapsLen = mipmaps.length;
|
||||
let glType = texture.type;
|
||||
let width = texture.width;
|
||||
@ -83,34 +93,22 @@ class GLTexture {
|
||||
|
||||
const updateTextureData = (
|
||||
gl: WebGL2RenderingContext,
|
||||
data: Texture2DData | TextureCubeData,
|
||||
data: Texture2DData | Texture3DData | Texture2DArrayData | TextureCubeData,
|
||||
level: number,
|
||||
width: number,
|
||||
height: number
|
||||
) => {
|
||||
if (isTexture2D) {
|
||||
this._updateTextureData2D(
|
||||
gl,
|
||||
data as Texture2DData,
|
||||
level,
|
||||
width,
|
||||
height,
|
||||
glInternalFormat,
|
||||
glFormat,
|
||||
glType
|
||||
);
|
||||
} else {
|
||||
this._updateTextureDataCube(
|
||||
gl,
|
||||
data as TextureCubeData,
|
||||
level,
|
||||
width,
|
||||
height,
|
||||
glInternalFormat,
|
||||
glFormat,
|
||||
glType
|
||||
);
|
||||
}
|
||||
this[`_update_${texture.textureType}`](
|
||||
gl,
|
||||
data as any,
|
||||
level,
|
||||
width,
|
||||
height,
|
||||
(texture as Texture3D).depth || 0,
|
||||
glInternalFormat,
|
||||
glFormat,
|
||||
glType
|
||||
);
|
||||
};
|
||||
|
||||
if (mipmapsLen) {
|
||||
@ -130,12 +128,13 @@ class GLTexture {
|
||||
this.unbind(gl);
|
||||
}
|
||||
|
||||
private _updateTextureData2D(
|
||||
private _update_texture2D(
|
||||
gl: WebGL2RenderingContext,
|
||||
data: Texture2DData,
|
||||
level: number,
|
||||
width: number,
|
||||
height: number,
|
||||
depth: number,
|
||||
glInternalFormat: GLEnum,
|
||||
glFormat: GLEnum,
|
||||
glType: GLEnum
|
||||
@ -188,12 +187,78 @@ class GLTexture {
|
||||
}
|
||||
}
|
||||
|
||||
private _updateTextureDataCube(
|
||||
private _update_texture2DArray(
|
||||
_gl: WebGL2RenderingContext,
|
||||
data: Texture2DArrayData,
|
||||
level: number,
|
||||
width: number,
|
||||
height: number,
|
||||
depth: number,
|
||||
glInternalFormat: GLEnum,
|
||||
glFormat: GLEnum,
|
||||
glType: GLEnum
|
||||
) {
|
||||
const source = data.image || data.pixels;
|
||||
if (source) {
|
||||
_gl.texStorage3D(
|
||||
constants.TEXTURE_2D_ARRAY,
|
||||
level,
|
||||
glInternalFormat,
|
||||
width,
|
||||
height,
|
||||
source.length
|
||||
);
|
||||
source.forEach((source, idx) =>
|
||||
_gl.texSubImage3D(
|
||||
constants.TEXTURE_2D_ARRAY,
|
||||
level,
|
||||
0,
|
||||
0,
|
||||
width,
|
||||
height,
|
||||
idx,
|
||||
0,
|
||||
glFormat,
|
||||
glType,
|
||||
source as HTMLImageElement
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private _update_texture3D(
|
||||
_gl: WebGL2RenderingContext,
|
||||
data: Texture3DData,
|
||||
level: number,
|
||||
width: number,
|
||||
height: number,
|
||||
depth: number,
|
||||
glInternalFormat: GLEnum,
|
||||
glFormat: GLEnum,
|
||||
glType: GLEnum
|
||||
) {
|
||||
const source = data.pixels;
|
||||
_gl.texImage3D(
|
||||
constants.TEXTURE_3D,
|
||||
level,
|
||||
glInternalFormat,
|
||||
width,
|
||||
height,
|
||||
depth,
|
||||
0,
|
||||
glFormat,
|
||||
glType,
|
||||
source || null
|
||||
);
|
||||
}
|
||||
|
||||
private _update_textureCube(
|
||||
_gl: WebGL2RenderingContext,
|
||||
data: TextureCubeData,
|
||||
level: number,
|
||||
width: number,
|
||||
height: number,
|
||||
depth: number,
|
||||
glInternalFormat: GLEnum,
|
||||
glFormat: GLEnum,
|
||||
glType: GLEnum
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user