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; }
最新评论
- 相关文章
CSS3原生变量(Native Variables)新特性简介
对Web开发者来说,一个盼望已久的特性是CSS终于支持原生变量了!
变量是程序语言中用来解决代码重复和进行表达式计算的关键概念(想想数学方程式中的x)。...HTML5动画背后的数学 - 粒子群仿生算法简介
本站收录了多个算法可视化动画,如模拟鸟群运动:http://wow.techbrood.com/fiddle/30529等等。这里面除...
使用SVG和CSS3创建圆形进度条动画
圆形进度条是一个经典的控制面板元素,常用于显示任务进度,比如用户档案的完整程度,或者升级状态。有很多方法来实现圆形进度条,比如用JS, CSS3, Canvas, SVG...
Three.js 对象局部坐标转换为世界坐标
在Three.js中进行顶点几何计算时,一个需要注意的地方是,需要统一坐标系。比如你通过Three.js提供的API创建了一个球体网孔对象,那么默认情况下,各网孔顶点的...
三维向量的简单运算和实用意义
在WebGL的实际应用中我们广泛使用向量的几何运算来计算角度、距离,判断点线、点面之间的关系,比如物体之间的碰撞检测。本文简要介绍三维计算机图形学中常用的...
粒子运动模拟 - Verlet积分算法简介
Verlet算法是经典力学(牛顿力学)中的一种最为普遍的积分方法,被广泛运用在分子运动模拟(Molecular Dynamics Simulation),行星运动以及织物变形模拟等领域...
如何使用Three.js加载obj和mtl文件
OBJ和MTL是3D模型的几何模型文件和材料文件。在最新的three.js版本(r78)中,以前的OBJMTLLoader类已废弃。现在要加载OBJ和MTL文件,需要结合OBJLoader和MTLLoade...
使用Canvas绘制完美的不完美圆形
真实世界是不完美的,当我们需要模拟真实世界时,经常需要引入不完美/不规则的形状。比如陨石、雨滴、行星、树叶、绵延的海岸线、云朵等。本文介绍如何基于Canva...
使用纯CSS3实现一个3D旋转的书本
有一些前沿的电商网站已经开始使用3D模型来展示商品并支持在线定制,而其中图书的展示是最为简单的一种,无需复杂的建模过程,使用图片和CSS3的一些变换即可实现...
如何使用CSS3实现书页(书本)卷角效果
我们有时候想在页面显示一个公告或用户提示信息。一个常用设计是使用书签形状。我们可以给书签添加卷角效果,以使其更为逼真。所谓的“卷角”实际上可以用小角度...
SVG过滤器feColorMatrix矩阵变换效果用法详解
在计算机图形学(数学)中,矩阵乘法可用于把空间向量进行几何变换。我们可以把颜色的值(RGBA)表示成一个四维空间向量:color = (r, g, b, a);那么就可以应用...
更多...