继前期分享了 初入webgl
的一些内容之后,相信大家已经对webgl
有了一个初步的认识,今天再来分享一些基础内容,已加深大家对于它的认知。
众所周知,3d
的内容是比较多的,也很容易让人看的很迷惑。所以,此系列文章的分享理念就一个 -- 说透彻,说清楚,说明白。
好了,话不多说,进入今天的分享内容。
1. 前言:
分享之前先来回顾一下两个内容:
-
webgl
中有几种类型的变量? - 如何使用缓冲区对象向着色器传递数据?
也希望大家带着这两个问题来阅读本篇文章。
2. varying变量的使用
前边的文章中介绍了 webgl
中的数据类型,现在我们来重温一下:
-
attribute
:影响单个顶点 -
uniform
:影响全部顶点 -
varying
:由顶点向片元传递数据
在之前文章的实例中也使用了 attribute、uniform
这两个变量。相信对于这两个变量的使用都有了一定程度的认识。但是对于最后一个 varying
变量,之前的文章中从未使用过。
由介绍可得知,varying
变量的主要作用就是从顶点着色器向片元着色器传值。具体怎么使用呢?莫慌,一步步来看下面的实现:
2.1 绘制一个点,(老代码,回顾一下。无新知识)
const VERTEX_SHADER_SOURCE = '' +
'attribute vec4 apos;' +
'void main () {' +
' gl_Position = apos;' +
' gl_PointSize = 10.0;' +
'}'
const FRAGMENT_SHADER_SOURCE = '' +
'precision lowp float;' +
'varying vec4 vColor;' +
'void main() {' +
' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);' +
'}' +
''
const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE);
const buffer = gl.createBuffer()
const data = new Float32Array([
-1.0, -1.0,
1.0, -1.0,
0.0, 1.0,
])
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW)
const apos = gl.getAttribLocation(program, 'apos');
gl.vertexAttribPointer(apos, 2, gl.FLOAT, false, 0, 0)
gl.enableVertexAttribArray(apos)
gl.drawArrays(gl.TRIANGLES, 0, 3)
这段代码相信大家已经非常熟悉,在画布中绘制一个红色的三角形,对于其中用到的方法也可以查下之前的文章。这里就不赘述了。
2.2 使用 varying 传递数据
使用 varying
传递数据需要分以下几步:
在顶点着色器中创建一个变量,用于获取数据。可暂时先用
apos
。将位置信息作为颜色传入到片元着色器中在顶点着色器中创建
varying
变量将数据
apos
赋值给varying
变量在片元着色器中指定精度
在片元着色器中创建同名的
varying
变量使用
varying
变量
注意:
varying
变量的类型与 attribute
的类型相同。也是四分量的浮点数 -- vec4
了解了以上步骤之后,来看下代码怎么写:
栗子:
const VERTEX_SHADER_SOURCE = '' +
'attribute vec4 apos;' + // 用于获取数据的 apos 变量
'varying vec4 vColor;' + // 用于传输数据的 varying 变量
'void main () {' +
' gl_Position = apos;' +
' gl_PointSize = 10.0;' +
' vColor = apos;' + // 将位置数据传输到片元着色器中
'}'
const FRAGMENT_SHADER_SOURCE = '' +
'precision lowp float;' + // 指定浮点数精度为低精度
'varying vec4 vColor;' + // 创建同名的 varying 变量
'void main() {' +
' gl_FragColor = vColor;' + // 使用 varying 变量
'}' +
''
再次运行后可以看到一个彩色的三角形。这里我们就将位置信息当做颜色信息传入到了片元着色器中。记得一定要指定精度。否则会出现渲染错误。
2.3 传入指定的颜色数据
在第二步中,通过传入位置信息来显示对应位置的颜色。接下来来看看,如何通过 varying
变量传入指定的颜色信息。
1. 首先修改着色器源代码。
const VERTEX_SHADER_SOURCE = '' +
'attribute vec4 apos;' +
'attribute vec4 aColor;' + // 新增颜色信息变量
'varying vec4 vColor;' +
'void main () {' +
' gl_Position = apos;' +
' gl_PointSize = 10.0;' +
' vColor = aColor;' + // 将颜色信息传入到 varying 变量中
'}'
const FRAGMENT_SHADER_SOURCE = '' +
'precision lowp float;' +
'varying vec4 vColor;' +
'void main() {' +
' gl_FragColor = vColor;' +
'}' +
''
2. 修改缓冲区数据。
// 在每个位置后新增颜色信息。vec4 的数据。
const data = new Float32Array([
-1.0, -1.0, 1.0, 1.0, 0.0, 1.0,
1.0, -1.0, 0.0, 1.0, 1.0, 1.0,
0.0, 1.0, 1.0, 0.0, 1.0, 1.0,
])
3. 修改数据传入
绘制三角形的时候只有一个 apos
变量。现在新增了一个,所以需要修改数据的传入方式。修改后的代码如下
const apos = gl.getAttribLocation(program, 'apos');
gl.vertexAttribPointer(apos, 2, gl.FLOAT, false, data.BYTES_PER_ELEMENT * 6, 0)
gl.enableVertexAttribArray(apos)
const aColor = gl.getAttribLocation(program, 'aColor');
gl.vertexAttribPointer(aColor, 4, gl.FLOAT, false, data.BYTES_PER_ELEMENT * 6, data.BYTES_PER_ELEMENT * 2);
gl.enableVertexAttribArray(aColor)
这里可以这样理解:
-
data
缓冲区中,每6
个元素表示一个顶点信息。 -
apos
需要两个,从每个顶点的第1
个数据开始查找。 -
aColor
需要四个,从每个顶点的第3
个数据开始查找。
同时对于代码中所涉及到的新知识点 gl.vertexAttribPointer、data.BYTES_PER_ELEMENT
两个内容,也来详细介绍下。
1.对于 gl.vertexAttribPointer
之前的文章里有过介绍,可能介绍的不太详细。再来详细介绍下:
gl.vertexAttribPointer(location, size, type, normalized, stride, offset);
location
: 指定修改的着色器变量的位置size
: 每次绘制需要几个顶点,定义的数据如何分配。type
:顶点的数据类型。与类型化数组的类型相同。
gl.BYTE
:带符号的8位整数gl.SHORT
:带符号的16位整数gl.UNSIGNED_BYTE
:无符号8位整数gl.UNSIGNED_SHORT
:无符号16位整数gl.FLOAT
:32位浮点数normalized
:是否将浮点数归一化到 [0, 1] 或 [-1, 1] 之间stride
:可简单理解为一个点需要多少个数据。offset
:偏移几个顶点开始绘制
这里也会注意到,最后的 stride 和 offset
两个参数都做了修改。而且是通过 data.BYTES_PER_ELEMENT
来实现的。
2. data.BYTES_PER_ELEMENT
表示数组中每个元素所占的字节数。主要有以下几个分类:
数组类型: 占的字节数
Int8Array; // 1
Uint8Array; // 1
Uint8ClampedArray; // 1
Int16Array; // 2
Uint16Array; // 2
Int32Array; // 4
Uint32Array; // 4
Float32Array; // 4
Float64Array; // 8
不出意外的话,经过这些操作,你会得到一个色彩鲜艳的彩色三角形。
3. 总结
接下来回顾一下文章开始提出的问题:
3.1 webgl
中有几种类型的变量?
有三种。
-
attribute
:影响单个顶点 -
uniform
:影响全部顶点 -
varying
:由顶点向片元传递数据
3.2 如何使用缓冲区对象向着色器传递数据?
- 创建
buffer
:gl.createBuffer()
- 创建缓冲区数据:
new Float32Array()
- 绑定缓冲区:
gl.bindBuffer
- 传入缓冲区数据:
gl.bufferData
- 获取变量:
const 获取到的变量 = gl.getAttribLocation(program, '变量名称')
- 给变量赋值:
gl.vertexAttribPointer(location, size, type, normalized, stride, offset)
- 激活变量:
gl.enableVertexAttribArray(获取到的变量)
4. 完整代码
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>顶点着色器向片元着色器传递数据</title>
</head>
<body>
<div>
<canvas id="canvas" width="300" height="300" style="border: 1px solid #666666;"></canvas>
</div>
<script>
const canvas = document.getElementById('canvas')
const gl = canvas.getContext('webgl')
const VERTEX_SHADER_SOURCE = '' +
'attribute vec4 apos;' +
'attribute vec4 aColor;' +
'varying vec4 vColor;' +
'void main () {' +
' gl_Position = apos;' +
' gl_PointSize = 10.0;' +
' vColor = aColor;' +
'}'
const FRAGMENT_SHADER_SOURCE = '' +
'precision lowp float;' +
'varying vec4 vColor;' +
'void main() {' +
' gl_FragColor = vColor;' +
'}' +
''
const vertexShader = gl.createShader(gl.VERTEX_SHADER)
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
gl.shaderSource(vertexShader, VERTEX_SHADER_SOURCE)
gl.shaderSource(fragmentShader, FRAGMENT_SHADER_SOURCE)
gl.compileShader(vertexShader)
gl.compileShader(fragmentShader)
const program = gl.createProgram()
gl.attachShader(program, vertexShader)
gl.attachShader(program, fragmentShader)
gl.linkProgram(program)
gl.useProgram(program)
const buffer = gl.createBuffer()
const data = new Float32Array([
-1.0, -1.0, 1.0, 1.0, 0.0, 1.0,
1.0, -1.0, 0.0, 1.0, 1.0, 1.0,
0.0, 1.0, 1.0, 0.0, 1.0, 1.0,
])
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW)
const apos = gl.getAttribLocation(program, 'apos');
gl.vertexAttribPointer(apos, 2, gl.FLOAT, false, data.BYTES_PER_ELEMENT * 6, 0)
gl.enableVertexAttribArray(apos)
/*
Int8Array; // 1
Uint8Array; // 1
Uint8ClampedArray; // 1
Int16Array; // 2
Uint16Array; // 2
Int32Array; // 4
Uint32Array; // 4
Float32Array; // 4
Float64Array; // 8
*/
const aColor = gl.getAttribLocation(program, 'aColor');
gl.vertexAttribPointer(aColor, 4, gl.FLOAT, false, data.BYTES_PER_ELEMENT * 6, data.BYTES_PER_ELEMENT * 2);
gl.enableVertexAttribArray(aColor)
gl.drawArrays(gl.TRIANGLES, 0, 3)
</script>
</body>
</html>