WebGL 3着色器和GLSL

我们之前提到过着色器和GLSL,但是没有涉及细节,你可能已经对此有所了解, 但以防万一,这里将详细讲解着色器和GLSL。

工作原理中我们提到,WebGL每次绘制需要两个着色器, 一个顶点着色器 和一个片段着色器 ,每一个着色器都是一个方法

一个顶点着色器和一个片段着色器链接在一起放入一个着色程序中(或者只叫程序)。

一个典型的WebGL应用会有多个着色程序。


顶点着色器

一个顶点着色器的工作是生成裁剪空间坐标值,通常是以下的形式

bash 复制代码
void main() {
   gl_Position = doMathToMakeClipspaceCoordinates
}

每个顶点调用一次(顶点)着色器,每次调用都需要设置一个特殊的全局变量gl_Position, 该变量的值就是裁剪空间坐标值。

顶点着色器需要的数据,可以通过以下三种方式获得。

Attributes 属性(从缓冲中获取的数据)

最常用的方法是缓冲和属性 ,在工作原理 中讲到了缓冲和属性,你可以创建缓冲

javascript 复制代码
var buf = gl.createBuffer();

将数据存入缓冲

javascript 复制代码
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, someData, gl.STATIC_DRAW);

然后初始化的时候,在你制作的(着色)程序中找到属性所在地址

javascript 复制代码
var positionLoc = gl.getAttribLocation(someShaderProgram, "a_position");

在渲染的时候告诉WebGL怎么从缓冲中获取数据传递给属性

javascript 复制代码
// 开启从缓冲中获取数据
gl.enableVertexAttribArray(positionLoc);
 
var numComponents = 3;  // (x, y, z)
var type = gl.FLOAT;    // 32位浮点数据
var normalize = false;  // 不标准化
var offset = 0;         // 从缓冲起始位置开始获取
var stride = 0;         // 到下一个数据跳多少位内存
                        // 0 = 使用当前的单位个数和单位长度 ( 3 * Float32Array.BYTES_PER_ELEMENT )
 
gl.vertexAttribPointer(positionLoc, numComponents, type, false, stride, offset);

属性可以用 float, vec2, vec3, vec4, mat2, mat3mat4 数据类型。

Uniforms 全局变量(在一次绘制中对所有顶点保持一致值)

全局变量在一次绘制过程中传递给着色器的值都一样。

在下面的一个简单的例子中, 用全局变量给顶点着色器添加了一个偏移量

bash 复制代码
attribute vec4 a_position;
uniform vec4 u_offset;
 
void main() {
   gl_Position = a_position + u_offset;
}

现在可以把所有顶点偏移一个固定值,首先在初始化时找到全局变量的地址

javascript 复制代码
var offsetLoc = gl.getUniformLocation(someProgram, "u_offset");

然后在绘制前设置全局变量

javascript 复制代码
gl.uniform4fv(offsetLoc, [1, 0, 0, 0]);  // 向右偏移一半屏幕宽度

要注意的是全局变量属于单个着色程序,如果多个着色程序有同名全局变量,需要找到每个全局变量并设置自己的值。 我们调用gl.uniform???的时候只是设置了当前程序 的全局变量,当前程序是传递给gl.useProgram 的最后一个程序

纹理(顶点着色器中)

Textures 纹理(在片段着色器中)

片段着色器

​​​​​​​一个片段着色器的工作是为当前光栅化的像素提供颜色值,通常是以下的形式

javascript 复制代码
precision mediump float;
 
void main() {
   gl_FragColor = doMathToMakeAColor;
}

每个像素都将调用一次片段着色器,每次调用需要从特殊全局变量gl_FragColor中获取颜色信息

Uniform 全局变量(片段着色器中)

Uniforms 全局变量.

Textures 纹理(片段着色器中 来自 Pixels/Texel 的数据)

在着色器中获取纹理信息,可以先创建一个sampler2D类型全局变量,然后用GLSL方法texture2D 从纹理中提取信息。

javascript 复制代码
precision mediump float;
 
uniform sampler2D u_texture;
 
void main() {
   vec2 texcoord = vec2(0.5, 0.5);  // 获取纹理中心的值
   gl_FragColor = texture2D(u_texture, texcoord);
}

从纹理中获取的数据取决于很多设置。 至少要创建并给纹理填充数据,例如

javascript 复制代码
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,   // 一个红色的像素
   0, 255, 0, 255,   // 一个绿色的像素
]);
gl.texImage2D(gl.TEXTURE_2D, level, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, data);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);

在初始化时找到全局变量的地址

javascript 复制代码
var someSamplerLoc = gl.getUniformLocation(someProgram, "u_texture");

在渲染的时候WebGL要求纹理必须绑定到一个纹理单元上

javascript 复制代码
var unit = 5;  // 挑选一个纹理单元
gl.activeTexture(gl.TEXTURE0 + unit);
gl.bindTexture(gl.TEXTURE_2D, tex);

然后告诉着色器你要使用的纹理在那个纹理单元

javascript 复制代码
gl.uniform1i(someSamplerLoc, unit);

Varyings 可变量(从 Vertex Shader 传递并插值的数据)

工作原理提到过,可变量是一种顶点着色器给片段着色器传值的方式。

为了使用可变量,要在两个着色器中定义同名的可变量。 给顶点着色器中可变量设置的值,会作为参考值进行内插,在绘制像素时传给片段着色器的可变量。

顶点着色器

cpp 复制代码
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;
}

片段着色器

bash 复制代码
precision mediump float;
 
varying vec4 v_positionWithOffset;
 
void main() {
  // 从裁剪空间 (-1 <-> +1) 转换到颜色空间 (0 -> 1).
  vec4 color = v_positionWithOffset * 0.5 + 0.5;
  gl_FragColor = color;
}

上方的示例几乎没有意义,通常情况下直接将裁剪空间的值传给片段着色器当作颜色值是没有意义的, 虽然它可以运行并且可以生成颜色值。

GLSL

GLSL全称是 Graphics Library Shader Language (图形库着色器语言),是着色器使用的语言。 它有一些不同于JavaScript的特性,主要目的是为栅格化图形提供常用的计算功能。

所以它内建的数据类型例如vec2, vec3vec4分别代表两个值,三个值和四个值, 类似的还有mat2, mat3mat4 分别代表 2x2, 3x3 和 4x4 矩阵。 你可以做一些运算例如常量和矢量的乘法

之后的一些调用方法 以及书写规则等可自行查看,在此就不在赘述

参考:

WebGL 着色器和GLSL

18.WebGL渲染和执行流程 | 前端技术积累

相关推荐
前端小崔4 小时前
从零开始学习three.js(18):一文详解three.js中的着色器Shader
前端·javascript·学习·3d·webgl·数据可视化·着色器
AllBlue1 天前
常见三维引擎坐标轴 webgl threejs cesium blender unity ue 左手坐标系、右手坐标系、坐标轴方向
blender·webgl·cesium
threelab1 天前
12.three官方示例+编辑器+AI快速学习webgl_buffergeometry_indexed
学习·编辑器·webgl
像素工坊可视化3 天前
WebGL 开发前沿:探索未来图形渲染的新可能
图形渲染·webgl
三天不学习3 天前
一文讲透 Vue3 + Three.js 材质属性之皮革篇【扫盲篇】
javascript·webgl·three.js·材质
Modify_QmQ4 天前
WebGL图形编程实战【7】:变换流水线 × 坐标系与矩阵精讲
webgl·视图变换·投影变换·ndc变换·视口变换
像素工坊可视化4 天前
WebGL 开发的前沿探索:开启 3D 网页的新时代
3d·webgl
threelab5 天前
18.three官方示例+编辑器+AI快速学习webgl_buffergeometry_points_interleaved
学习·编辑器·webgl
Modify_QmQ6 天前
WebGL图形编程实战【6】:性能优化 × 调试工具与技巧精讲
性能优化·webgl·webgl-lint·webgl-inspector