WebGL基础知识 - GLSL和着色器(Shader)
在本站的WebGL入门教程中,提到绘制管道中有两个着色器,一个是vertex shader(顶点着色器)和一个fragment shader(片段着色器)。本章简介这两个着色器的具体使用。
每个着色器本质上就是一个函数,有特定的输入和输出。着色器函数被串联到同一个着色器程序中。
Vertex Shader
顶点着色器的功能是把原始顶点数据变换到裁减空间坐标。每个顶点都会调用该着色器函数。
顶点着色器的输入数据有如下2种方式:
Attributes (从缓存中获取的数据)
Uniforms (单次绘制中对所有顶点保持不变的值)
Attributes
最常见的方式是通过缓存和属性。首先创建缓存:
var buf = gl.createBuffer();
写入数据:
gl.bindBuffer(gl.ARRAY_BUFFER, buf); gl.bufferData(gl.ARRAY_BUFFER, someData, gl.STATIC_DRAW);
然后, 给定一个着色程序,您可以在初始化时查找其属性的位置:
var positionLoc = gl.getAttribLocation(someShaderProgram, "a_position");
在渲染时告诉WebGL如何从缓存中读出数据并写入属性中:
// 启用此属性从缓冲区中获取数据的功能 gl.enableVertexAttribArray(positionLoc); var numComponents = 3; // (x, y, z) var type = gl.FLOAT; // 32bit浮点数 var normalize = false; // 保持数据原样,不做规范化 var offset = 0; // 从缓存的起点处开始 var stride = 0; // 移动到下一个顶点的步幅 // 0 = 使用符合当前type和numComponents取值的步幅 gl.vertexAttribPointer(positionLoc, numComponents, type, false, stride, offset);
在着色器函数中使用属性数据如下(这里尚未作任何数学处理):
attribute vec4 a_position; void main() { gl_Position = a_position; }
属性(Attributes)可以使用的数据类型有:float, vec2, vec3, vec4, mat2, mat3 和 mat4。
Uniforms
Uniforms是传递给着色器的值,这些值对于绘制调用中的所有顶点保持不变。
比如我们可以向上面的顶点着色器添加一个偏移量:
attribute vec4 a_position; uniform vec4 u_offset; void main() { gl_Position = a_position + u_offset; }
这样我们就可以将每个顶点偏移一定的量。
为此,首先我们需要在初始化时得到该uniform的位置:
var offsetLoc = gl.getUniformLocation(someProgram, "u_offset");
然后在渲染前给uniform设置值:
gl.uniform4fv(offsetLoc, [1, 0, 0, 0]); //将其偏移到屏幕的右半部分
注意,这里uniforms是属于单个着色器程序的,如果你的WebGL应用程序包含多个着色器程序,那么在不同的shader中的同名uniform将拥有他们各自的存储位置和取值。当调用gl.uniformXXX方法时我们只是设置当前着色器程序(shader program)的uniform,当前shader program就是你最近一次调用gl.useProgram所应用的那个。
Uniforms有很多类型,对于每个类型都有对应的方法来设置它的值:
gl.uniform1f (floatUniformLoc, v); // for float gl.uniform1fv(floatUniformLoc, [v]); // for float or float array gl.uniform2f (vec2UniformLoc, v0, v1); // for vec2 gl.uniform2fv(vec2UniformLoc, [v0, v1]); // for vec2 or vec2 array gl.uniform3f (vec3UniformLoc, v0, v1, v2); // for vec3 gl.uniform3fv(vec3UniformLoc, [v0, v1, v2]); // for vec3 or vec3 array gl.uniform4f (vec4UniformLoc, v0, v1, v2, v4); // for vec4 gl.uniform4fv(vec4UniformLoc, [v0, v1, v2, v4]); // for vec4 or vec4 array gl.uniformMatrix2fv(mat2UniformLoc, false, [ 4x element array ]) // for mat2 or mat2 array gl.uniformMatrix3fv(mat3UniformLoc, false, [ 9x element array ]) // for mat3 or mat3 array gl.uniformMatrix4fv(mat4UniformLoc, false, [ 16x element array ]) // for mat4 or mat4 array gl.uniform1i (intUniformLoc, v); // for int gl.uniform1iv(intUniformLoc, [v]); // for int or int array gl.uniform2i (ivec2UniformLoc, v0, v1); // for ivec2 gl.uniform2iv(ivec2UniformLoc, [v0, v1]); // for ivec2 or ivec2 array gl.uniform3i (ivec3UniformLoc, v0, v1, v2); // for ivec3 gl.uniform3iv(ivec3UniformLoc, [v0, v1, v2]); // for ivec3 or ivec3 array gl.uniform4i (ivec4UniformLoc, v0, v1, v2, v4); // for ivec4 gl.uniform4iv(ivec4UniformLoc, [v0, v1, v2, v4]); // for ivec4 or ivec4 array gl.uniform1i (sampler2DUniformLoc, v); // for sampler2D (textures) gl.uniform1iv(sampler2DUniformLoc, [v]); // for sampler2D or sampler2D array
比如设置一个二维矢量的数组:
// in shader uniform vec2 u_someVec2[3]; // in JavaScript at init time var someVec2Loc = gl.getUniformLocation(someProgram, "u_someVec2"); // at render time gl.uniform2fv(someVec2Loc, [1, 2, 3, 4, 5, 6]);
Fragment Shader
顶点着色器的输出数据经过光栅化处理后,输入给片段着色器,而片段着色器的功能就是为正在光栅化的当前像素提供颜色。
每个像素都会调用片段着色器。片段着色器的输入数据有如下3种方式:
Uniforms (对于单个绘图调用的每个像素保持相同的值,同上)
Textures (从像素pixels和纹元texels中读取的数据)
Varyings (从顶点着色器传递并插值的数据)
Textures
首先在shader中创建一个sampler2d uniform,我们可以使用glsl函数texture2d从中提取值(需要提供纹理坐标):
precision mediump float; uniform sampler2D u_texture; void main() { vec2 texcoord = vec2(0.5, 0.5) // 从texture的中间位置获取数据 gl_FragColor = texture2D(u_texture, texcoord); }
在WebGL程序初始化时得到shader中u_texture的位置:
var someSamplerLoc = gl.getUniformLocation(someProgram, "u_texture");
然后我们需要设置texture的数据(数据格式有很多种),下面是一个简单的示例数据:
var tex = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, tex); var level = 0; var width = 2; var height = 1; var data = new Uint8Array([ 255, 0, 0, 255, // 1个红色像素 0, 255, 0, 255, // 1个绿色像素 ]); gl.texImage2D(gl.TEXTURE_2D, level, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, data);
在渲染时告诉WebGL激活某texture单元,并绑定tex到该单元(比如gl.TEXTURE0),然后告诉着色器把someSampler关联到该单元:
var unit = 0; gl.activeTexture(gl.TEXTURE + unit); gl.bindTexture(gl.TEXTURE_2D, tex); gl.uniform1i(someSamplerLoc, unit);
通过上面一连串的绑定关联动作,我们就通过WebGL程序接口把2个像素数据设置到shader中的u_texture中,并最终给到gl_FragColor。
Varyings
变量(Varyings)是一种将值从顶点着色器传递到片段着色器的方法。和常量(Uniforms)不同的是,该值可以被顶点着色器修改,然后在片段着色器中读取(只读)。变量的值是一个插值数据,每个像素都不同。
要使用变量,我们需要在顶点和片段着色器中声明匹配的变量。我们用每个顶点的值来设置顶点着色中的变化。当WebGL绘制像素时,它将在这些值之间进行插值,并将它们传递给片段明暗器中相应的变量。
Vertex shader
attribute vec4 a_position; uniform vec4 u_offset; varying vec4 v_positionWithOffset; void main() { gl_Position = a_position + u_offset; v_positionWithOffset = a_position + u_offset; }
Fragment shader
precision mediump float; varying vec4 v_positionWithOffset; void main() { // convert from clipsapce (-1 <-> +1) to color space (0 -> 1). vec4 color = v_positionWithOffset * 0.5 + 0.5 gl_FragColor = color; }


最新评论
- 相关文章
HTML5 And Canvas 2D Specs Are Now Feature Complete, First HTML 5.1 Working Draft Published
We’ve been writing about HTML5 for quite a while, but, until today, the actual HTML5 specs and standards were still moving targets. Now, however, the...
Blender2.7给平面模型添加纹理贴图
在blender中给模型添加纹理,需要有2个步骤:首先在对象属性栏中给该对象添加材料和纹理建立纹理映射添加材料和纹理这是常见操作,略过步骤。但是仅仅这样操作,...
ES6小知识:动态对象键(Dynamic Object Keys)语法简介
在ES5,对象的键(key)总是被解释为字符串。ES6允许我们使用计算的值作为对象的键,使用方括号:[myKey]const
JavaScript语言多编程范式简介
和C++等语言类似,JS支持多范式(paradigms)编程。我们常常混合这些范式来完成一些大型Web项目。JS支持3种编程范式:命令式、面向对象和函数式。命令式(Imperative JavaScript)命令式就是简单的从上而下完成任务,流水账过程式编码风格:function
HTML5动画背后的数学 - 粒子群仿生算法简介
本站收录了多个算法可视化动画,如模拟鸟群运动:http://wow.techbrood.com/fiddle/30529等等。这里面除...
计算WebGL中的uniforms变量使用数
在使用Three.js为人体模型加载皮肤材料时,启用了skinning:true的参数。有时候会导致GL编译错误,提示“too many uniforms”。下面的文章有助于理解错误原因和检...
Three.js 对象局部坐标转换为世界坐标
在Three.js中进行顶点几何计算时,一个需要注意的地方是,需要统一坐标系。比如你通过Three.js提供的API创建了一个球体网孔对象,那么默认情况下,各网孔顶点的...
Blender2.7 快捷键一览表
通用操作
停止当前操作:ESC
快捷搜索:SPACE撤销:ctrl+z重做:ctrl+shift+z渲染:F12
单选:鼠标右键(RMB)全选:A
框选:B
刷选:...如何使用Three.js加载obj和mtl文件
OBJ和MTL是3D模型的几何模型文件和材料文件。在最新的three.js版本(r78)中,以前的OBJMTLLoader类已废弃。现在要加载OBJ和MTL文件,需要结合OBJLoader和MTLLoade...
Three.js入门教程5 - 10个必须知道的编程技巧
作者为Google的Paul,关于如何写出好的WebGL代码的文章。和很多开发者一样,我通过实践学习,但同时我也向其他更有经验的开发者们学习。在过去的几个月中,我在c...
WebGL入门教程4 - 使用纹理贴图(Texture Map)
3D建模和纹理贴图的关系就好比人体和皮肤(或着装)的关系,3D建模用来处理空间属性,而贴图适合用来处理细腻的表面属性。如果不使用贴图,而想在表面达到足够的...
HTML网页布局:静态、自适应、流式、响应式
静态布局(Static Layout)即传统Web设计,对于PC设计一个Layout,在屏幕宽高有调整时,使用横向和竖向的滚动条来查阅被遮掩部分;对于移动设备,单独设计一个布...
如何使用CSS3实现一个3D商品标签
使用3D缎带形状的标签是常见的一个设计模式,用在商品折扣、文章标题或网站推荐信息上,来突出显示重点内容,吸引用户视觉焦点。实现的方法有2种,一种是使用背...
更多...