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; }
最新评论
- 相关文章
OpenGL/WebGL顶点坐标变换过程简介
世界坐标是按照笛卡尔坐标系定义出来的绝对坐标系,下面的各种坐标系都建立在世界坐标的基础上。对象坐标系对象被应用于任何...
创建非矩形网页页面元素的常用技术和实例代码
非矩形设计正在变成一种时尚,比如波浪形、菱形、三角形等:而随着技术发展,这种设计在技术实现上也变得更容易。本文以最简单的三角形为例,演示使用5种方法来...
CSS3原生变量(Native Variables)新特性简介
对Web开发者来说,一个盼望已久的特性是CSS终于支持原生变量了!
变量是程序语言中用来解决代码重复和进行表达式计算的关键概念(想想数学方程式中的x)。...使用CSS3 box-decoration-break特性实现多行文本样式
当文章中的长文本被自动断行为多行文本时,其样式可能会出乎我们的设计。本文介绍如何使用CSS3中的box-decoration-break特性来处理多行元素样式。
按照规范...深入理解CSS3滤镜(filter)功能和实例详解
CSS3滤镜功能源自SVG滤镜规范,SVG滤镜最早用来给矢量图添加类似PS中像素图的一些特效。
把这个滤镜功能引入到普通HTML元素中可以带来很有趣的效果(模糊、...HTML5动画背后的数学 - 粒子群仿生算法简介
本站收录了多个算法可视化动画,如模拟鸟群运动:http://wow.techbrood.com/fiddle/30529等等。这里面除...
深入理解Three.js(WebGL)贴图(纹理映射)和UV映射
本文将详细描述如何使用Three.js给3D对象添加贴图(Texture Map,也译作纹理映射,“贴图”的翻译要更直观,而“纹理映射”更准确。)。为了能够查看在线演示效...
Three.js入门教程6 - 创建全景图和纹理
全景图非常酷。使用Three.js做一个属于自己的全景图并不是那么困难。要做一个全景图,你需要一个软件用来做一张全景图片。我使用了iPhone上的Microsoft Photosyn...
如何实现SVG clipPath自适应被裁剪对象
CSS3中引入的clip-path(裁剪路径)属性是一个很强大的特性。
clip-path的含义如下图所示,好比剪纸一样,你用剪刀沿着某条路径把目标对象(图像或元素)裁...jQuery Ribbles - 基于WebGL的水面涟漪动效插件
使用jQuery
如何使用纯CSS3实现一个3D泡沫
要实现一个逼真的泡沫,涉及到比较复杂的光学/物理学知识。我们这里先简化下问题,实现一个相对简单而足够实用的泡沫元素。我们可以把基础的泡沫元素应用在很多场景中,比如水景、泡咖啡、啤酒甚至火焰特效中。泡沫首先是一个圆形元素.bubble
如何使用CSS3/SCSS实现逼真的车窗雨滴效果
在天气预报类的Web移动应用中,常常需要实现屏幕的雨滴效果,表示阴雨天气。感觉上比较神奇,其实想通了,这个效果的实现只需要一点物理知识和CSS3的简单变换。实现一个小雨滴首先雨滴是一个个小的椭圆形元素:.raindrop
更多...