We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JavaScript WebGL 设置颜色效果始终有限,这时候就会想到使用图片,这就涉及到 WebGL 中的纹理使用,比预想中要麻烦的多。
纹理(texture)可以用来添加模拟物体的细节,在 3D 游戏中各种模拟的物体都使用了纹理。在绘制矩形的基础上主要有以下几个方面的变化:
先准备一张图片,然后为了把纹理映射到对应的矩形上,需要指定矩形每个顶点各自对应纹理的那个位置。
纹理二维坐标在 x 和 y 轴上,范围为 0 到 1 之间。纹理坐标起于(0, 0),对应图片的左下角,终于(1, 1),对应图片的右上角。所以对应的纹理坐标为:
const texCoords = [ 1.0, 1.0, // 右上角 0.0, 1.0, // 左上角 0.0, 0.0, // 左下角 1.0, 0.0, // 右下角 ];
纹理坐标需要进行缓冲并进行传递,在顶点着色器中增加了变量 aVertexTextureCoord ,其值会传递到片段着色器。
aVertexTextureCoord
const source = ` attribute vec3 aVertexPos; attribute vec2 aVertexTextureCoord; varying highp vec2 vTextureCoord; void main(void){ gl_Position = vec4(aVertexPos, 1); vTextureCoord = aVertexTextureCoord; } `;
在片段着色器中接受纹理坐标,定义纹理采样器 uSampler ,注意这个是全局变量,在任何阶段都可以访问,目前还没有值。内置的方法 texture2D 获得最终的颜色。
uSampler
texture2D
const fragmentSource = ` varying highp vec2 vTextureCoord; uniform sampler2D uSampler; void main(void){ gl_FragColor = texture2D(uSampler, vTextureCoord); } `;
纹理坐标数据同样需要进入缓冲。
/** * 缓冲纹理坐标数据并激活 * @param {*} gl WebGL 上下文 * @param {*} shaderProgram 着色器程序 * @param {*} data 纹理坐标数据 */ function setTextureBuffers(gl, shaderProgram, data) { // 创建空白的缓冲对象 const buffer = gl.createBuffer(); // 绑定目标 gl.bindBuffer(gl.ARRAY_BUFFER, buffer); // WebGL 不支持直接使用 JavaScript 原始数组类型,需要转换 const dataFormat = new Float32Array(data); // 初始化数据存储 gl.bufferData(gl.ARRAY_BUFFER, dataFormat, gl.STATIC_DRAW); // 获取对应数据索引 const texCoord = gl.getAttribLocation( shaderProgram, "aVertexTextureCoord" ); // 解析顶点数据 gl.vertexAttribPointer(texCoord, 2, gl.FLOAT, false, 0, 0); // 启用顶点属性,顶点属性默认是禁用的。 gl.enableVertexAttribArray(texCoord); }
要保证图片加载完成后才能使用。得到图片数据后需要创建纹理对象。
function loadImage(gl) { var img = new Image(); img.onload = (e) => { createTexture(gl, e.target); }; img.src = "./1.jpg"; } function createTexture(gl, source) { const texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); // 反转图片 Y 轴方向 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); // 纹理坐标水平填充 s gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); // 纹理坐标垂直填充 t gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); // 纹理放大处理 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); // 纹理缩小处理 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); // 图片数据赋给纹理对象 gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source ); // 激活纹理 gl.activeTexture(gl.TEXTURE0); }
createTexture 创建纹理对象,接着使用 bindTexture 并绑定到对应的目标,这里是二维的图片,第一个参数值取 gl.TEXTURE_2D 表示二维纹理,第二个参数是纹理对象,当为 null 时表示取消绑定。绑定之后才能对纹理进行进一步操作。
gl.TEXTURE_2D
null
pixelStorei 方法对图像进行反转 Y 方向坐标,这是因为图片的坐标系统和纹理参考的坐标系不一样。
texParameteri 方法设置纹理的各种参数,这里需要特地说明一下,如果希望使用各种尺寸的图片,需要对水平和垂直填充进行上面的设置,否则只能显示特定尺寸的图片。
texImage2D 方法把纹理源赋值给纹理对象,这里就是把图像的像素数据传给纹理对象,这样才能在绘制纹理的时候看到图像。
activeTexture 方法激活指定的纹理,纹理单元的范围是 0 到 gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS - 1 ,这里只有一个,取值为 gl.TEXTUREI0 。默认的第一个纹理单元总是激活的,所以这行代码可以去掉。
gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS - 1
gl.TEXTUREI0
在片段着色器中声明的全局变量,绘制时使用 uniform1i 方法指定对应的值,第二个参数表示纹理单元,这里 0 就是第一个纹理单元。
/** * 绘制 * @param {*} gl WebGL 上下文 * @param {*} shaderProgram 着色器程序 */ function draw(gl, shaderProgram) { // 获取纹理采样器 const samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); // 指定全局变量关联的纹理单元 gl.uniform1i(samplerUniform, 0); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); }
这是示例, 效果如下:
如果对比原图的话,可以发现这张图片变形了,并没有自适应。
最近《英雄联盟:双城之战》 很火,去看了下的确精彩,即使不玩这个游戏同样看得明白。
估计会吸引一些新的玩家。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
目录
引子
JavaScript WebGL 设置颜色效果始终有限,这时候就会想到使用图片,这就涉及到 WebGL 中的纹理使用,比预想中要麻烦的多。
使用图片
纹理(texture)可以用来添加模拟物体的细节,在 3D 游戏中各种模拟的物体都使用了纹理。在绘制矩形的基础上主要有以下几个方面的变化:
数据
先准备一张图片,然后为了把纹理映射到对应的矩形上,需要指定矩形每个顶点各自对应纹理的那个位置。
纹理二维坐标在 x 和 y 轴上,范围为 0 到 1 之间。纹理坐标起于(0, 0),对应图片的左下角,终于(1, 1),对应图片的右上角。所以对应的纹理坐标为:
顶点着色器
纹理坐标需要进行缓冲并进行传递,在顶点着色器中增加了变量
aVertexTextureCoord
,其值会传递到片段着色器。片段着色器
在片段着色器中接受纹理坐标,定义纹理采样器
uSampler
,注意这个是全局变量,在任何阶段都可以访问,目前还没有值。内置的方法texture2D
获得最终的颜色。缓冲纹理坐标数据
纹理坐标数据同样需要进入缓冲。
加载并创建纹理
要保证图片加载完成后才能使用。得到图片数据后需要创建纹理对象。
createTexture 创建纹理对象,接着使用 bindTexture 并绑定到对应的目标,这里是二维的图片,第一个参数值取
gl.TEXTURE_2D
表示二维纹理,第二个参数是纹理对象,当为null
时表示取消绑定。绑定之后才能对纹理进行进一步操作。pixelStorei 方法对图像进行反转 Y 方向坐标,这是因为图片的坐标系统和纹理参考的坐标系不一样。
texParameteri 方法设置纹理的各种参数,这里需要特地说明一下,如果希望使用各种尺寸的图片,需要对水平和垂直填充进行上面的设置,否则只能显示特定尺寸的图片。
texImage2D 方法把纹理源赋值给纹理对象,这里就是把图像的像素数据传给纹理对象,这样才能在绘制纹理的时候看到图像。
activeTexture 方法激活指定的纹理,纹理单元的范围是 0 到
gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS - 1
,这里只有一个,取值为gl.TEXTUREI0
。默认的第一个纹理单元总是激活的,所以这行代码可以去掉。绘制
在片段着色器中声明的全局变量,绘制时使用 uniform1i 方法指定对应的值,第二个参数表示纹理单元,这里 0 就是第一个纹理单元。
效果
这是示例, 效果如下:
如果对比原图的话,可以发现这张图片变形了,并没有自适应。
参考资料
🗑️
最近《英雄联盟:双城之战》 很火,去看了下的确精彩,即使不玩这个游戏同样看得明白。
估计会吸引一些新的玩家。
The text was updated successfully, but these errors were encountered: