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属性选择器特性使用详解
CSS3除了引入动画、滤镜(用于特效)以及新的布局技术外,在选择器(selector)方面也有增强。属性选择器根据元素的属性(attributes)来匹配。这可以是一个单独...
JavaScript语言多编程范式简介
和C++等语言类似,JS支持多范式(paradigms)编程。我们常常混合这些范式来完成一些大型Web项目。JS支持3种编程范式:命令式、面向对象和函数式。命令式(Imperative JavaScript)命令式就是简单的从上而下完成任务,流水账过程式编码风格:function
常见面试题JavaScript闭包(ES5语法)
JavaScript闭包(Closure)是常见的JS面试题,是否理解闭包是一个简单的区分JS初级和高级程序员的判例。几乎每个JS程序员都在使用闭包,有意或无意间。比如编写一个jQuery鼠标点击处理函数:$(function()
Web界面编程状态变化和JS开发框架(React/Angular/Ember)
UI编程中的一个关键课题就是界面组件化(可复用)以及组件状态管理。稍早一些的windows程序员可能接触过MFC,其界面编程中有一个DDX(DoDataExchange)的机制,...
CSS3弹性布局弹性流(flex-flow)属性详解和实例
弹性布局是CSS3引入的强大的布局方式,用来替代以前Web开发人员使用的一些复杂而易错hacks方法(如使用float进行类似流式布局)。其中flex-flow是flex-direction...
HTML5动画背后的数学2 - 仿生智能算法综述
使用SVG和CSS3创建圆形进度条动画
圆形进度条是一个经典的控制面板元素,常用于显示任务进度,比如用户档案的完整程度,或者升级状态。有很多方法来实现圆形进度条,比如用JS, CSS3, Canvas, SVG...
三维向量的简单运算和实用意义
在WebGL的实际应用中我们广泛使用向量的几何运算来计算角度、距离,判断点线、点面之间的关系,比如物体之间的碰撞检测。本文简要介绍三维计算机图形学中常用的...
纹理基础知识和过滤模式详解
1、 为什么在纹理采样时需要texture filter(纹理过滤)。
我们的纹理是要贴到三维图形表面的,而三维图形上的pixel中心和纹理上的texel中心并不一至(pixe...Blender2.7 快捷键一览表
通用操作
停止当前操作:ESC
快捷搜索:SPACE撤销:ctrl+z重做:ctrl+shift+z渲染:F12
单选:鼠标右键(RMB)全选:A
框选:B
刷选:...CSS3图片混合(Blend)效果及其参考计算公式一览表
在Photoshop软件中,混合是将两个图层的色彩值进行合成,从而创造出大量的效果。在这些效果的背后实际是一些简单的数学公式在起作用。下面所介绍的公式仅适用于R...
WebGL入门教程6 - 光照效果和Phong光照模型
正是因为有了光,世界才能被我们看见,在3D的世界里,光照给物体带来真实的视觉感受。当光照射在某一表面上时,它可能被吸收、反射或投射。其中入射到表面上的一...
IE各版本CSS Hack(兼容性处理)语法速查表
为了兼容IE各个版本,需要在CSS中添加额外的代码,比如以前常用的_width。之所以工作,是因为浏览器会忽略不能解析的样式规则,因此举个例子来说,把_width写在w...
如何基于Canvas来模拟真实雨景Part1:预备知识和创建基本对象
div 、section 、article的区别和使用场景
div 、section 、article的区别和使用场景
主要区别,以及适用场合如下:
1、div在html早期版本就支持了,section和article是html5提出的两个雨衣话标... 更多...