diff --git a/src/App.tsx b/src/App.tsx index 865b80e6..c8212b77 100755 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,6 +1,7 @@ import React from 'react'; import './App.css'; -import { vec3, vec4 } from "./render/math" +import { vec3, vec4 } from "./render/math"; +import {} from "./render/webgl/WebGLTexture"; // eslint-disable-next-line function App() { diff --git a/src/render/common.ts b/src/render/common.ts index 5f7b3857..951ac5cf 100755 --- a/src/render/common.ts +++ b/src/render/common.ts @@ -1 +1,33 @@ -export let EPSILON: number = 0.0001; \ No newline at end of file +export let EPSILON: number = 0.0001; + +export enum EShaderType { + VS_SHADER, + FS_SHADER +} + +export enum EGLSLESDataType { + FLOAT_VEC2 = 0x8B50, + FLOAT_VEC3, + FLOAT_VEC4, + INT_VEC2, + INT_VEC3, + INT_VEC4, + BOOL, + BOOL_VEC2, + BOOL_VEC3, + BOOL_VEC4, + FLOAT_MAT2, + FLOAT_MAT3, + FLOAT_MAT4, + SAMPLER_2D, + SAMPLER_CUBE, + + FLOAT = 0x1406, + INT = 0x1404 +} + +export enum EGLTexWrapType { + GL_REPEAT, //设置为gl对应的常量 + GL_MIRRORED_REPEAT, + GL_CLAMP_TO_EDGE +} \ No newline at end of file diff --git a/src/render/webgl/WebGLHepler.ts b/src/render/webgl/WebGLHepler.ts new file mode 100755 index 00000000..e943ced4 --- /dev/null +++ b/src/render/webgl/WebGLHepler.ts @@ -0,0 +1,285 @@ +import { EShaderType, EGLSLESDataType } from "../common"; + + +/** + * uniform参数 + */ +export class GLUniformInfo { + public size: number; + public type: EGLSLESDataType; + public location: WebGLUniformLocation; + + public constructor(size: number, type: number, loc: WebGLUniformLocation) { + this.size = size; + this.type = type; + this.location = loc; + } +} + +export class GLAttribInfo { + public size: number; + public type: EGLSLESDataType; + public location: number; + public constructor(size: number, type: number, loc: number) { + this.size = size; + this.type = type; + this.location = loc; + } +} + +export type GLUinformMap = { [key: string]: GLUniformInfo }; +export type GLAttribMap = { [key: string]: GLAttribInfo }; + +export class GLHelper { + + /** + * 打印webgl上下文的各个状态 + * @param gl + */ + public static printStates(gl: WebGLRenderingContext): void { + // 所有的boolean状态变量,共9个 + console.log("1. isBlendEnable = " + gl.isEnabled(gl.BLEND)); + console.log("2. isCullFaceEnable = " + gl.isEnabled(gl.CULL_FACE)); + console.log("3. isDepthTestEnable = " + gl.isEnabled(gl.DEPTH_TEST)); + console.log("4. isDitherEnable = " + gl.isEnabled(gl.DITHER)); + console.log("5. isPolygonOffsetFillEnable = " + gl.isEnabled(gl.POLYGON_OFFSET_FILL)); + console.log("6. isSampleAlphtToCoverageEnable = " + gl.isEnabled(gl.SAMPLE_ALPHA_TO_COVERAGE)); + console.log("7. isSampleCoverageEnable = " + gl.isEnabled(gl.SAMPLE_COVERAGE)); + console.log("8. isScissorTestEnable = " + gl.isEnabled(gl.SCISSOR_TEST)); + console.log("9. isStencilTestEnable = " + gl.isEnabled(gl.STENCIL_TEST)); + } + + /** + * 打印webgl信息 + * @param gl + */ + public static printWebGLInfo(gl: WebGLRenderingContext): void { + console.log("renderer = " + gl.getParameter(gl.RENDERER)); + console.log("version = " + gl.getParameter(gl.VERSION)); + console.log("vendor = " + gl.getParameter(gl.VENDOR)); + console.log("glsl version = " + gl.getParameter(gl.SHADING_LANGUAGE_VERSION)); + } + + /** + * 获取textture信息 + * @param gl + */ + public static printWebGLTextureInfo(gl: WebGLRenderingContext): void { + console.log("MAX_COMBINED_TEXTURE_IMAGE_UINTS = ", gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS)); + console.log("MAX_TEXTURE_IMAGE_UNITS = ", gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS)); + console.log("MAX_TEXTURE_SIZE = ", gl.getParameter(gl.MAX_TEXTURE_SIZE)); + console.log("MAX_CUBE_MAP_TEXTURE_SIZE", gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE)); + } + + /** + * 触发context lose 事件 + * @param gl + */ + public static triggerContextLosEvent(gl: WebGLRenderingContext): void { + let context: WEBGL_lose_context | null = gl.getExtension('WEBGL_lose_context'); + if (context !== null) { + context.loseContext(); + } + } + + /** + * 获取webgl错误 + * @param gl + */ + public static checkGLError(gl: WebGLRenderingContext): boolean { + let err: number = gl.getError(); + if (err === 0) { + return false; + } else { + console.log("WebGL ERROR NO: ", err); + return true; + } + } + + /** + * 设置webgl的默认状态 + * @param gl + */ + public static setDefaultState(gl: WebGLRenderingContext): void { + gl.clearColor(0.0, 0.0, 0.0, 0.0);//每次清屏时,将颜色缓冲区设置全透明 黑色 + gl.clearDepth(1.0);//每次清屏时,将深度缓冲区设置成1.0 + gl.enable(gl.DEPTH_TEST); //每次清屏时候开始深度测试 + gl.enable(gl.CULL_FACE);//每次清屏时候开始背面剔除 + gl.enable(gl.SCISSOR_TEST);//每次清屏时候开启剪切测试 + } + + /** + * 设置webgl的视口 + * @param gl + * @param v + */ + public static setViewport(gl: WebGLRenderingContext, v: number[]) { + gl.viewport(v[0], v[1], v[2], v[3]); + } + + /** + * 创建着色器 + * @param gl + * @param type + * @returns + */ + public static createShader(gl: WebGLRenderingContext, type: EShaderType): WebGLShader { + let ret: WebGLShader | null = null; + + if (type === EShaderType.VS_SHADER) { + ret = gl.createShader(gl.VERTEX_SHADER); + } else { + ret = gl.createShader(gl.FRAGMENT_SHADER); + } + + if (ret === null) { + throw new Error("WebGLShader create failed!"); + } + return ret; + } + + /** + * 编译着色器 + * @param gl + * @param code + * @param shader + * @returns + */ + public static compileShader(gl: WebGLRenderingContext, code: string, shader: WebGLShader): boolean { + gl.shaderSource(shader, code);//载入shader源码 + gl.compileShader(shader); + if (gl.getShaderParameter(shader, gl.COMPILE_STATUS) === false) { + alert(gl.getShaderInfoLog(shader));//如果编译出错,弹出出错信息 + gl.deleteShader(shader); + return false; + } + return true; + } + + /** + * 创建链接器对象 + * @param gl + * @returns + */ + public static createProgram(gl: WebGLRenderingContext): WebGLProgram { + let ret: WebGLProgram | null = gl.createProgram(); + if (ret === null) { + throw new Error("WebGLProgram create falied!"); + } + return ret; + } + + /** + * + * @param gl 渲染上下文对象 + * @param program 链接器对象 + * @param vsShader 顶点着色器 + * @param fsShader 片段着色器 + * @param beforeProgramLink 链接前执行函数 + * @param afterProgramLink 链接后执行函数 + */ + public static linkProgram( + gl: WebGLRenderingContext, + program: WebGLProgram, + vsShader: WebGLShader, + fsShader: WebGLShader, + beforeProgramLink: ((gl: WebGLRenderingContext, program: WebGLProgram) => void) | null = null, + afterProgramLink: ((gl: WebGLRenderingContext, program: WebGLProgram) => void) | null = null + ): boolean { + + gl.attachShader(program, vsShader); + gl.attachShader(program, fsShader); + + if (beforeProgramLink !== null) { + beforeProgramLink(gl, program); + } + + gl.linkProgram(program); + if (gl.getProgramParameter(program, gl.LINK_STATUS) === false) { + alert(gl.getProgramInfoLog(program)); + gl.deleteShader(vsShader); + gl.deleteShader(fsShader); + gl.deleteProgram(program); + + return false; + } + + gl.validateProgram(program); + if (gl.getProgramParameter(program, gl.VALIDATE_STATUS) === false) { + alert(gl.getProgramInfoLog(program)); + gl.deleteShader(vsShader); + gl.deleteShader(fsShader); + gl.deleteProgram(program); + + return false; + } + + if (afterProgramLink !== null) { + afterProgramLink(gl, program); + } + + return true; + } + + /** + * 获取生效的attribute + * @param gl + * @param program + * @param out + */ + public static getProgramActiveAttribs(gl: WebGLRenderingContext, program: WebGLProgram, out: GLAttribMap): void { + + let count: number = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);//获取当前active状态的attribute的数量, active_attributes/uniforms必须在link后才能获得 + + for (let i = 0; i < count; i++) { + let info: WebGLActiveInfo | null = gl.getActiveAttrib(program, i); + if (info) { + out[info.name] = new GLAttribInfo(info.size, info.type, gl.getAttribLocation(program, info.name)); + } + } + } + + /** + * 获取生效的uniform + * @param gl + * @param program + * @param out + */ + public static getProgramActiveUniforms(gl: WebGLRenderingContext, program: WebGLProgram, out: GLUinformMap): void { + let count: number = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); + + for (let i = 0; i < count; i++) { + let info: WebGLActiveInfo | null = gl.getActiveUniform(program, i); + if (info) { + let loc: WebGLUniformLocation | null = gl.getUniformLocation(program, info.name); + if (loc !== null) { + out[info.name] = new GLUniformInfo(info.size, info.type, loc); + } + } + } + } + + /** + * 创建一个webgl buffer + * @param gl + * @returns + */ + public static createBuffer(gl: WebGLRenderingContext): WebGLBuffer { + let ret: WebGLBuffer | null = gl.createBuffer(); + if (ret === null) { + throw new Error("webgl create buffer failed!"); + } + return ret; + } + + /** + * 创建color buffer + * @param gl + * @returns + */ + public static getColorBufferData(gl: WebGLRenderingContext): Uint8Array { + let ret: Uint8Array = new Uint8Array(gl.drawingBufferWidth * gl.drawingBufferHeight * 4); + gl.readPixels(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight, gl.RGBA, gl.UNSIGNED_BYTE, ret); + return ret; + } +} \ No newline at end of file diff --git a/src/render/webgl/WebGLMesh.ts b/src/render/webgl/WebGLMesh.ts new file mode 100755 index 00000000..66d79a52 --- /dev/null +++ b/src/render/webgl/WebGLMesh.ts @@ -0,0 +1,3 @@ +export abstract class GLMeshBase { + +} \ No newline at end of file diff --git a/src/render/webgl/WebGLProgram.ts b/src/render/webgl/WebGLProgram.ts new file mode 100755 index 00000000..7648b302 --- /dev/null +++ b/src/render/webgl/WebGLProgram.ts @@ -0,0 +1,3 @@ +export class GLProgram { + +} \ No newline at end of file diff --git a/src/render/webgl/WebGLTexture.ts b/src/render/webgl/WebGLTexture.ts new file mode 100755 index 00000000..365a78f1 --- /dev/null +++ b/src/render/webgl/WebGLTexture.ts @@ -0,0 +1,75 @@ +import { EGLTexWrapType } from "../common"; +import { GLHelper } from "./WebGLHepler"; + + +export class GLTexture { + + public gl: WebGLRenderingContext; + public isMipmap: boolean; //是否使用了mipmap + public width: number; + public height: number; + public format: number; //在内存或显存中的存储格式,默认是gl.RGBA + public type: number; //像素的数据类型,默认是gl.UNSIGNED_BYTE + public texture: WebGLTexture; + public target: number; //gl.TEXTURE_2D + public name: string; //纹理的名称 + + public constructor(gl: WebGLRenderingContext, name: string = '') { + this.gl = gl; + this.isMipmap = false; + this.width = this.height = 0; + this.format = gl.RGBA; + this.type = gl.UNSIGNED_BYTE; + let tex: WebGLTexture | null = gl.createTexture(); + if (tex === null) { + throw new Error("WebGL texture create falied!"); + } + this.texture = tex; + this.target = gl.TEXTURE_2D; + this.name = name; + this.wrap(); + this.filter(); + } + + /** + * 判断是否是2的倍数 + * @param x + * @returns + */ + public static isPowerOfTwo(x: number) { + return (x & (x - 1)) == 0; + } + + /** + * 纹理包装设置 + * @param mode + */ + public wrap(mode: EGLTexWrapType = EGLTexWrapType.GL_REPEAT): void { + this.gl.bindTexture(this.target, this.texture); + if (mode === EGLTexWrapType.GL_CLAMP_TO_EDGE) { + this.gl.texParameteri(this.target, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE); + this.gl.texParameteri(this.target, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE); + } else if (mode === EGLTexWrapType.GL_REPEAT) { + this.gl.texParameteri(this.target, this.gl.TEXTURE_WRAP_S, this.gl.REPEAT); + this.gl.texParameteri(this.target, this.gl.TEXTURE_WRAP_T, this.gl.REPEAT); + } else { + this.gl.texParameteri(this.target, this.gl.TEXTURE_WRAP_S, this.gl.MIRRORED_REPEAT); + this.gl.texParameteri(this.target, this.gl.TEXTURE_WRAP_T, this.gl.MIRRORED_REPEAT); + } + } + + /** + * 设置纹理滤波 + * @param minLinear + * @param magLinear + */ + public filter(minLinear: boolean = true, magLinear: boolean = true): void { + this.gl.bindTexture(this.target, this.texture); + if (this.isMipmap) { + this.gl.texParameteri(this.target, this.gl.TEXTURE_MIN_FILTER, minLinear ? this.gl.LINEAR_MIPMAP_LINEAR : this.gl.NEAREST_MIPMAP_NEAREST); + } else { + this.gl.texParameteri(this.target, this.gl.TEXTURE_MIN_FILTER, minLinear ? this.gl.LINEAR : this.gl.NEAREST); + } + this.gl.texParameteri(this.target, this.gl.TEXTURE_MIN_FILTER, magLinear ? this.gl.LINEAR : this.gl.NEAREST); + } +} \ No newline at end of file