颜色与纹理
-
将非坐标数据传入顶点着色器
-
步骤(同将顶点坐标传入着色器):
创建缓冲区对象
将缓冲区对象绑定target上
将非坐标数据写入缓冲区对象
将缓冲区对象分配给对应的attribute变量
开启attribute变量
-
关键代码:
// 顶点着色器程序 var VSHADER_SOURCE = 'attribute vec4 a_Position;\n' + 'attribute float a_PointSize;\n' + 'void main() {\n' + ' gl_Position = a_Position;\n' + ' gl_PointSize = a_PointSize;\n' + '}\n'; var FSHADER_SOURCE = 'void main() {\n' + ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + '}\n'; var sizes = new Float32Array([ 10.0, 20.0, 30.0 // 点的尺寸 ]); // 创建缓冲区对象 var vertexBuffer = gl.createBuffer(); var sizeBuffer = gl.createBuffer(); // 将顶点坐标写入缓冲区对象并开启 gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); var a_Position = gl.getAttribLocation(gl.program, 'a_Position'); if(a_Position < 0) { console.log('Failed to get the storage location of a_Position'); return -1; } gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(a_Position); // 将顶点尺寸写入缓冲区对象并开启 gl.bindBuffer(gl.ARRAY_BUFFER, sizeBuffer); gl.bufferData(gl.ARRAY_BUFFER, sizes, gl.STATIC_DRAW); var a_PointSize = gl.getAttribLocation(gl.program, 'a_PointSize'); if(a_PointSize < 0) { console.log('Failed to get the storage location of a_PointSize'); return -1; } gl.vertexAttribPointer(a_PointSize, 1, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(a_PointSize);
-
-
创建多个缓冲区对象
vertexBuffer:存储顶点坐标数据
sizeBuffer:存储顶点尺寸数据
-
gl.vertexAttribPointer()的步进和偏移参数
使用多个缓冲区对象向着色器传递多种数据,比较适合数据量不大的情况。当程序中的复杂三维图形具有成千上万个顶点时,WebGL允许我们把顶点的坐标和尺寸数据打包到同一个缓冲区对象中,并通过某种机制分别访问缓冲区对象中不同种类的数据。
-
交错组织
关键代码
// 顶点坐标和点的尺寸 0.0, 0.5, 10.0, // 第一个点 -0.5, -0.5, 20.0, // 第二个点 0.5, -0.5, 30.0 // 第三个点 ]); var FSIZE = verticesSizes.BYTES_PER_ELEMENT; gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 3, 0); gl.vertexAttribPointer(a_PointSize, 1, gl.FLOAT, false, FSIZE * 3, FSIZE * 2);
[stride] # 在缓冲区对象中,单个顶点的所有数据的字节数,也就是相邻两个顶点间的距离,即步进参数 [offset] # 当前考虑的数据项距离首个元素的距离。即偏移参数
-
-
修改颜色
-
varying 变量:从顶点着色器向片元着色器传输数据
关键代码
var VSHADER_SOURCE = 'attribute vec4 a_Position;\n' + 'attribute vec4 a_Color;\n' + 'varying vec4 v_Color;\n' + // varying变量 'void main() {\n' + ' gl_Position = a_Position;\n' + ' gl_PointSize = 10.0;\n' + ' v_Color = a_Color;\n' + // 将数据传给片元着色器 '}\n'; var FSHADER_SOURCE = 'precision mediump float;\n' + 'varying vec4 v_Color;\n' + // 从顶点着色器接收数据 'void main() {\n' + ' gl_FragColor = v_Color;\n' + '}\n'; var verticesColors = new Float32Array([ // 顶点坐标和颜色 0.0, 0.5, 1.0, 0.0, 0.0, -0.5, -0.5, 0.0, 1.0, 0.0, 0.5, -0.5, 0.0, 0.0, 1.0, ]); gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 5, FSIZE * 2);
在WebGL中,如果顶点着色器与片元着色器中有类型和命名都相同的varying变量,那么顶点着色器赋给该变量的值就会自动地传入片元着色器
-
-
彩色三角形
图形装配过程:将孤立的顶点坐标装配成几何图形
-
光栅化过程:将装配好的几何图形转化为片元
调用片元着色器:片元颜色会随着片元位置逐渐变化,三角形呈现平滑的颜色渐变效果
varying变量的作用和内插过程:一条两个端点的颜色不同的线段,在顶点着色器中向varying变量赋上两个颜色值,WebGL会自动计算出线段上的所有点的颜色,并赋值给片元着色器中的varying变量。线段上的所有片元的颜色值都会被恰当地计算出来的过程被称为内插过程。
[计算机图形学] # Computer Graphics
-
在矩形表面贴上图像
-
纹理映射:将一张图映射到一个几何图形的表面上。这张图可以称为纹理图像或纹理。纹理映射的作用,就是根据纹理图像,为之前光栅化后的每个片元涂上合适的颜色。组成纹理图像的像素又称为纹素,每一个纹素的颜色都使用RGB或RGBA格式编码
-
步骤:
准备好映射到几何图形上的纹理图像
为几何图形配置纹理映射方式
加载纹理图像,对其进行一些配置,以在WebGL中使用它
在片元着色器中将相应的纹素从纹理中抽取出来,并将纹素的颜色赋给片元
-
纹理坐标(st坐标系统):纹理图像上的坐标,左下角(0.0,0.0),右下角(1.0,0.0),右上角(1.0,1.0),左上角(0.0,1.0)
-
将纹理图像粘贴到几何图形上
实现步骤:
顶点着色器中接收顶点的纹理坐标,光栅化后传递给片元着色器
片元着色器根据片元的纹理坐标,从纹理图像中抽取出纹素颜色,赋给当前片元
设置顶点的纹理坐标
准备待加载的纹理图像,令浏览器读取它
监听纹理图像的加载事件,一旦加载完成,就在WebGL系统中使用纹理
关键代码
var VSHADER_SOURCE = 'attribute vec4 a_Position;\n' + 'attribute vec2 a_TexCoord;\n' + 'varying vec2 v_TexCoord;\n' + 'void main() {\n' + ' gl_Position = a_Position;\n' + ' v_TexCoord = a_TexCoord;\n' + '}\n'; var FSHADER_SOURCE = '#ifdef GL_ES\n' + 'precision mediump float;\n' + '#endif\n' + 'uniform sampler2D u_Sampler0;\n' + 'uniform sampler2D u_Sampler1;\n' + 'varying vec2 v_TexCoord;\n' + 'void main() {\n' + ' vec4 color0 = texture2D(u_Sampler0, v_TexCoord);\n' + ' vec4 color1 = texture2D(u_Sampler1, v_TexCoord);\n' + ' gl_FragColor = color0 * color1;\n' + '}\n'; // 配置纹理 if (!initTextures(gl, n)) { console.log('Failed to intialize the texture.'); return; } var verticesTexCoords = new Float32Array([ // Vertex coordinates, texture coordinate -0.5, 0.5, 0.0, 1.0, -0.5, -0.5, 0.0, 0.0, 0.5, 0.5, 1.0, 1.0, 0.5, -0.5, 1.0, 0.0, ]); // 将顶点坐标和纹理坐标写入缓冲区对象 gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexCoordBuffer); gl.bufferData(gl.ARRAY_BUFFER, verticesTexCoords, gl.STATIC_DRAW); // 将纹理坐标分配给a_TexCoord并开启它 var a_TexCoord = gl.getAttribLocation(gl.program, 'a_TexCoord'); function initTextures(gl, n) { var texture = gl.createTexture(); // 创建纹理对象 if (!texture) { console.log('Failed to create the texture object'); return false; } // 获取u_Sampler的存储位置 var u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler'); if (!u_Sampler) { console.log('Failed to get the storage location of u_Sampler'); return false; } var image = new Image(); // 创建一个image对象 if (!image) { console.log('Failed to create the image object'); return false; } // 注册图像加载事件的响应函数 image.onload = function(){ loadTexture(gl, n, texture, u_Sampler, image); }; // 浏览器开始加载图像 image.src = '../resources/sky.jpg'; return true; } function loadTexture(gl, n, texture, u_Sampler, image) { gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1); // 对纹理图像进行y轴翻转(图片坐标系统和st坐标系统的Y轴方向相反) // 开启0号纹理单元 gl.activeTexture(gl.TEXTURE0); // 向target绑定纹理对象 gl.bindTexture(gl.TEXTURE_2D, texture); // 配置纹理参数 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); // 配置纹理图像 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image); // 将0号纹理传递给着色器 gl.uniform1i(u_Sampler, 0); gl.clear(gl.COLOR_BUFFER_BIT); gl.drawArrays(gl.TRIANGLE_STRIP, 0, n); }
-
使用多幅纹理:对每一幅纹理分别进行前面所述的将纹理图像映射到图形表面的操作,以此来将多张纹理图像同时贴到图形上去。
关键代码:
var FSHADER_SOURCE = '#ifdef GL_ES\n' + 'precision mediump float;\n' + '#endif\n' + 'uniform sampler2D u_Sampler0;\n' + 'uniform sampler2D u_Sampler1;\n' + 'varying vec2 v_TexCoord;\n' + 'void main() {\n' + ' vec4 color0 = texture2D(u_Sampler0, v_TexCoord);\n' + ' vec4 color1 = texture2D(u_Sampler1, v_TexCoord);\n' + ' gl_FragColor = color0 * color1;\n' + '}\n'; function initTextures(gl, n) { var texture0 = gl.createTexture(); var texture1 = gl.createTexture(); if (!texture0 || !texture1) { console.log('Failed to create the texture object'); return false; } var u_Sampler0 = gl.getUniformLocation(gl.program, 'u_Sampler0'); var u_Sampler1 = gl.getUniformLocation(gl.program, 'u_Sampler1'); if (!u_Sampler0 || !u_Sampler1) { console.log('Failed to get the storage location of u_Sampler'); return false; } var image0 = new Image(); var image1 = new Image(); if (!image0 || !image1) { console.log('Failed to create the image object'); return false; } image0.onload = function(){ loadTexture(gl, n, texture0, u_Sampler0, image0, 0); }; image1.onload = function(){ loadTexture(gl, n, texture1, u_Sampler1, image1, 1); }; image0.src = '../resources/sky.jpg'; image1.src = '../resources/circle.gif'; return true; } // 标记纹理单元是否已经就绪 var g_texUnit0 = false, g_texUnit1 = false; if (texUnit == 0) { gl.activeTexture(gl.TEXTURE0); g_texUnit0 = true; } else { gl.activeTexture(gl.TEXTURE1); g_texUnit1 = true; } gl.uniform1i(u_Sampler, texUnit); // Pass the texure unit to u_Sampler if (g_texUnit0 && g_texUnit1) { gl.drawArrays(gl.TRIANGLE_STRIP, 0, n); // Draw the rectangle }
无法预测哪一幅纹理图像先被加载完成,因为加载的过程是异步进行的
-