WebGL基础概念
-
WebGL需要提供成对的方法。没对方法中一个叫做顶点着色器,一个叫做片段着色器。再着色器语言中使用给GL着色语言(GLSL)
-
顶点着色器的作用是计算顶点位置(包括了根据三角形的三个顶点进行三角形内部各种属性的插值计算)
-
对图元进行光栅化处理时需要使用片段着色器。片段着色器的作用是计算出当前绘制图元中每个像素的颜色值。
-
这些方法对所需的任何数据都需要发送给GPU,着色器获取数据的四种方法
-
Attribute属性与缓冲
- 缓冲是发送到GPU的一系列的二进制数据,这些数据通常包括了(顶点坐标,顶点法向量,顶点的纹理坐标,顶点的颜色值)
- 属性用于指明怎么从缓冲中或许相应的数据并提供给顶点着色器。(比如可以使用三个4B的浮点型数据3fv去存储一个顶点坐标)(对于一个确切的属性需要指明从那个缓冲中获取数据,获取什么类型的数据(3fv),起始的偏移值是多少,到下一个位置的字节数是多少)
- 缓冲不是随意读取的。事实上顶点着色器运行的次数是一个确切的数字。每一次运行属性,都会从指定的缓冲中按照指定规则依次获取下一个值。
-
全局变量(uniforms)全局变量再着色程序运行之前赋值,再整个着色程序运行中全局有效。
-
纹理(Textures)纹理:纹理是一个数据序列,可以在着色程序运行中随意读取其中的数据。大多数情况下存放的是图像数据,也可以存放出了颜色数据之外的数据
-
可变量(varying):可变量是一种顶点着色器给片段着色器传值的方式,依照渲染的的图元是点,线还是三角形,顶点着色器中设置的可变量(Varying)会在片段着色器运行中获取不同的插值。
WebGL Hello World
- WebGL只关心两件事:裁剪空间中的坐标值和颜色值,也就是说使用WebGL只需要给他提供两个变量即可。其中,需要两个着色器来做这件事。其中,顶点着色器提供裁剪空间的坐标值,片段着色器提供颜色值。
- 也就是说无论画布有多大,裁剪空间的坐标范围永远是[-1,1]
- 从简单的顶点着色器开始
// 一个属性值,着色器会从缓冲中读取数据
attribute vec4 a_position;
// 所有着色器都有一个main方法
void main()
{
// gl_Position 是一个顶点着色器主要设置的变量
gl_Position = a_position;
}
如果使用了Javascript代替里顶点着色器语言,相当于在其中做了一下
// 定义了一个顶点缓冲
var positionBuffer = [0, 0, 0, 0,
0 ,1, 1, 2,
2 ,3, 3, 4];
var attributes = {};
val gl_Position;
drawArrays(..., offset, count)
{
// 每次取数据的间隔
var stride = 4;
var size = 4;
for(let i = 0; i < count; i ++)
{
// 从positionBuffer复制接下来4个值给a_Position属性
const start = offset + i * stride;
attributes.a_Position = positionBuffer.slice(start, start + size);
// 运行顶点着色器
runVertexShader();
...
doSomeThingWith_gl_Position();
}
}
接下来会需要一个片段着色器
// 片段着色器没有默认精度, 所以需要手动设置一个精度
// mediup 代表的是 medium precision(中等精度)
precision mediump float;
void main()
{
// gl_FragColor是一个片段着色器主要设置的变量
gl_FragColor = vec4(1, 0, 0.5, 1);
//1 代表红色值,0代表绿色值,0.5代表蓝色值,1代表阿尔法通道值
}
现在拥有了顶点着色器和片段着色器,可以开始使用WebGL了
- 首先需要一个HTML中的canvas(画布)对象
<canvas id="c"></canva>
然后就可以使用JavaScript获取画布
var canvas = document.querySelector("#c")
获取画布后需要创建一个WebGL渲染上下文(WebGLRenderingContext)
var gl = canvas.getContext("#webgl")
if(!gl)
{
console.log("上下文管理器创建失败");
}
之后需要编译着色器后提交到GPU中。一种方法是利用JavaScript中创建字符串的方式创建一个GLSL字符串。或者直接将其放在非JavaScript类型的标签中。
<script id="vertex-shader-2d" type="notjs">
// 一个属性变量,将会从缓冲中获取数据
attribute vec4 a_position;
// 所有着色器都有一个main方法
void main() {
// gl_Position 是一个顶点着色器主要设置的变量
gl_Position = a_position;
}
</script>
<script id="fragment-shader-2d" type="notjs">
// 片段着色器没有默认精度,所以我们需要设置一个精度
// mediump是一个不错的默认值,代表“medium precision”(中等精度)
precision mediump float;
void main() {
// gl_FragColor是一个片段着色器主要设置的变量
gl_FragColor = vec4(1, 0, 0.5, 1); // 返回“瑞迪施紫色”
}
</script>
接下来使用方法船舰一个着色器,只需要上传以及写好的GLSL数据,之后编译成为着色器即可。
// 创建着色器方法,输入参数:渲染的上下文gl,着色器类型,数据源(着色器源码)
function createShader(gl, type, source)
{
// 创建着色器对象
var shader = gl.createShader(type);
// 提供着色器源码
gl.shaderSource(shader, source);
// 编译生成着色器
gl.***pileShader(shader);
// 判断是否生成成功
if(gl.getShaderParameter(shader, gl.***PILE_STATUS) == true)
{
return shader;
}
else
{
console.log(gl.getShaderInfoLog(shader));
gl.deleterShader(shader);
}
}
之后便可以用这个函数创建两个shader程序(一个顶点着色器程序,一个片段着色器程序)
var vertexshadersource = document.querySelector('#vertex-shader-2d').text;
var fragshadersource = document.querySelector('#fragment-shader-2d').text;
var vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexshadersource);
var fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragshadersource);
之后可以将两个(顶点着色器和片段着色器连接到一个独有的着色程序上)
function createProgram(gl, vertexshader, fragmentshader)
{
var program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
// boolean变量判断是否创建成功
var su***ess = gl.getProgramParameter(program. gl.LINK_STATUS);
if(su***ess)
{
return program;
}
else
{
console.log(gl.getProgramInfoLog(program));
gl.deleteProgram(program);
}
}
之后就可以调用function生成一个GLSL着色程序
var program = createProgram(gl, vertexShader, fragmentShader);
现在已经在GPU上成功创建了一个着色程序,我们需要给其提供数据。WebGL的主要任务就是设置好状态并给GLSL着色程序提供所需的数据。在这个例子中GLSL着色程序的唯一输入就是一个属性值Attribute(a_position)。
我们要做的第一件事就是从着色程序中找到这个属性值所在的位置,可以使用以下方法
var a_positionAttributeLocation = gl.getAttribLocation(program, "a_position");
寻找属性值位置(以及全局变量)的位置应该在初始化完成,而不是在渲染循环中。
属性值从缓冲中获取数据,需要创建一个缓冲
var positionbuffer = gl.createBuffer();
WebGL可以通过绑定点操控全局的许多数据,可以把绑定点想象为一个WebGL内部的全局变量。首先帮点一个数据源到绑定点(ARRAY_BUFFER),然后就可以引用绑定点指向该数据源。绑定位置信息的缓冲
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer)
现在我们需要通过绑定点向缓冲中存放数据
可以理解为:绑定点就是外界与缓冲区狡猾数据的中介
是的,您的理解是正确的。在 WebGL 中,绑定点(binding point)可以被看作是在 WebGL 内部用于引用和操作数据的“全局变量”。绑定点充当了一种中介,使得您可以在 WebGL 的上下文中操控和引用数据。具体来说:
创建和绑定缓冲区:
- 首先,您创建了一个缓冲区(
positionBuffer
),这是用于存储数据(如顶点位置信息)的内存区域。- 然后,通过
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
将这个缓冲区绑定到gl.ARRAY_BUFFER
这个绑定点上。这个操作实际上是告诉 WebGL:“现在我要操作的是positionBuffer
这个缓冲区。”绑定点作为数据操作的参照:
- 绑定缓冲区到
ARRAY_BUFFER
后,之后的所有对ARRAY_BUFFER
的操作都会作用于当前绑定的缓冲区,即positionBuffer
。- 例如,当您使用函数像
gl.bufferData()
或gl.bufferSubData()
来向缓冲区传输数据时,这些数据会被存储到当前绑定到ARRAY_BUFFER
的缓冲区中(在这个例子中,就是positionBuffer
)。绑定点简化了数据管理:
- 由于 WebGL 是一个基于状态的绘图系统,绑定点机制简化了数据和资源的管理。您不需要在每次操作时都指定具体的缓冲区,而只需引用相应的绑定点。
- 这样的设计使得您可以在不同的渲染阶段方便地切换和管理不同的数据资源。
总之,绑定点在 WebGL 中起到了连接您的程序和 WebGL 内部数据存储(如缓冲区)之间的桥梁作用,使得数据的上传、修改和引用变得更加高效和方便。
现在我们需要通过绑定点向缓冲区中存放数据
// 三个二维点坐标
var positions = [
0, 0,
0, 0.2,
0.7, 0
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
这里完成了一系列事情
- 第一件事就是有了一个JavaScript序列数据,他代表的是顶点的坐标
- new Float32Array创建了一个32为浮点型数据序列,并将其从positions中复制到该序列中
- gl.bufferData复制这些数据到已经创建好的Buffer(positionBuffer)上(这里ARRAY_BUFFER就充当了一个外界和GPU内部positionBuffer交换数据的中介)
- 最后一个参数gl.STATIC_DRAW提示WebGL我们会如何操作这些数据,STATIC提示webGL我们不会经常改变这些数据
以上都是初始化渲染程序以及向渲染程序中传递数据的初始化代码,只会在页面加载时运行依次,接下来的便是渲染代码,这些代码会在我们每次需要渲染以及绘制时执行
渲染
在绘制之前我们应该调整画布(canvas)的尺寸以匹配它的显示尺寸。
因为画布就像图片,一般拥有两个尺寸。一个是其拥有的实际的像素数量,一个是它显示的带线啊哦。CSS可以决定画布显示的大小。
在渲染中。
我们需要告诉web GL怎样把提供的gl_position即裁剪空间的坐标如何对应带画布的像素坐标(画布的像素坐标就可以叫做window coordinates,或者是屏幕坐标screen coordinates),需要进行的变换的矩阵即视口变换矩阵,即viewportmatrix,为了实现这个目的,我们只需要调用gl.viewport方法传递画布当前画布的当前尺寸
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
这样就可以告诉WebGL裁剪空间的(-1,1)对应到x轴的屏幕空间的范围为(0,gl.canvas.width),对应到y轴的屏幕空间的范围为(0,gl.canva.height)。
我们可以使用(0,0,0,0)清空画布,分别对应着rgb,aplha值,使用全零清空画布后可以让画布 变得透明
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
之后需要告诉WebGL运行哪一个着色程序
gl.useProgram(program);
// 告诉它用我们之前写好的着色程序,一个着色对
接下来我们需要告诉WebGL,,怎么从之前准备的缓冲中,,,从缓冲中获取数据,,获取的数据提供给着色器中的属性。
首先需要启用对应的属性
gl.enableVertexAttribArray(positionAttributeLocation);
然后指定从缓冲中读取数据的方式
// 将绑定点绑定到缓冲数据
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// 告诉属性怎么从positionBuffer(ARRAY_BUFFER)中读取数据
var size = 2; // 每次迭代提取两个单位数据
var type = gl.FLOAT; // 每个单位的数据类型是32位浮点型
var normalize = false; // 不需要将数据归一化
var stride = 0; // 每次迭代运行运动多少内存到下一个数据开始点
var offset = 0; // 从缓冲起始位置开始读取
gl.vertexAttribPointer(
positionAttributeLocation, size, type, normalize, stride, offset);
一个隐藏信息是gl.vertexAttribPointer是将属性绑定到当前的ARRAY_BUFFER,换句话说就是属性绑定到了positionBuffer上,这也意味着现在利用绑定点随意将ARRAY_BUFFER绑定到其他数据上后,该属性依然会从positionBuffer上读取数据。
"use strict";
main()
function createshader(gl, type, source)
{
var shader = gl.createshader(type);
gl.shaderSource(shader, source);
gl.***pileShader(shader);
var su***ess = gl.getShaderParameter(shader, gl.***PILE_STATUS);
if(su***ess)
{
return shader;
}
console.log(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
}
function createProgram(gl, vertexShader, fragmentShader)
{
var program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
var su***ess = gl.getProgramParameter(program, gl.LINK_STATUS);
if(su***ess)
{
return program;
}
console.log(gl.getProgramInfoLog(program));
gl.deleteProgram(program);
}
function main()
{
// 获得一个webgl上下文
var canvas = document.querySelector("#c");
var gl = canvas.getContext("webgl");;
if(!gl){
return ;
}
// 获得顶点着色器和片段着色器的源码
var vertesshadersource = document.querySelector("#vertex-shader-2d").textContent;
var fragmentshadersource = document.querySelector("#fragment-shader-2d").textContent;
// create glsl shader, upload the glsl source, ***pile the shader
var vertexShader = createShader(gl, gl.VERTEX_SHADER, vertesshadersource);
var fragmentShader = createShader(gl, gl.FRAGMEN_SHADER, fragmentshadersource);
// link the shader into a shader program
var program = createProgram(gl, vertexShader, fragmentShader);
// look up where the vertex data needs to go
var positionAttributeLocation = gl.getAttribLocation(program, "a_position");
// create a buffer to put three 2D clip space points in it
var positionBuffer = gl.createBuffer();
// bind it to ARRAY_BUFFER(即ARRAY_BUFFER是数据和GPU空间的媒介)
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// create a new array of positions
var positions = [
0 , 0,
1 , 2,
3 , 4 ]
// 把数据放到BUFFER中
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
// 以下的代码是渲染时需要运行的代码
webglUtils.resizeCanvasToDisplaySize(gl.canvas);
// Tell webgl how to convert from clip space to window space
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// clear the canvas
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT); // opaque
// tell the webgl to use the shader program
gl.useProgram(program);
// 使用指向buffer的位置属性(启用指针)
gl.enableVertexAttribArray(positionAttributeLocation);
// tell the attribute how to get data out of positionBuffer
var size = 2; // 2 ***ponents per iteration
var type = gl.FLOAT; // the data is 32bit floats
var normalize = false; // don't normalize the data
var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
var offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(positionAttributeLocation, size, type, normalize, stride, offset);
// draw the triangle
var primitiveType = gl.TRIANGES;
var offset = 0;
var count = 3;
gl.drawArrays(primitiveType, offset, count);
}
着色语言GLSL时如下
<canvas id="c"></canvas>
<script id="vertex-shader-2d" type="notjs">
// an attribute will receive data from a buffer
attribute vec4 a_position;
// all shaders have a main function
void main() {
// gl_Position is a special variable a vertex shader
// is responsible for setting
gl_Position = a_position;
}
</script>
<script id="fragment-shader-2d" type="notjs">
// fragment shaders don't have a default precision so we need
// to pick one. mediump is a good default
precision mediump float;
void main() {
// gl_FragColor is a special variable a fragment shader
// is responsible for setting
gl_FragColor = vec4(0.8, 0.2, 0.9, 1); // return redish-purple
}
其中只有一个变量需要传递,就是编译后program中规定的attribute变量a_positions,在GLSL中规定的Attribute时需要WebGL传递给着色器程序的变量。通过接口gl.vertexAttribPointer
函数进行数据的传递
WebGL工作原理
WebGL在GPU上工作基本上分为两个部分,第一部分是将顶点(或数据流)转换位裁剪空间中的坐标,第二部分是基于第一部分的结果取绘制像素点
当调用
var primitiveType = gl.TRIANGLES;
var offset = 0;
var count = 9;
gl.drawArrays(primitiveType, offset, count);
这里count = 9所以会处理九次顶点信息
左侧是传入Buffer的数据信息,(vertex shader)是写入GL的一个方法,每个顶点都会被vertex shader去调用一次,在这个方法中做一些在vertex shader中定义的方法之后,设置了一个特殊的变量gl_Position
保存起来,这个变量就是将顶点从局部坐标系转换为裁剪空间的坐标值(这里定义的全局变量uniform u_matrix对应的就是MVP变换矩阵),GPU会接收gl_Position并将其保存。
假设你正在画三角形。顶点着色器每完成三次顶点处理后,WebGL就会用三个顶点画出三角形。也就是所谓的,计算出三个顶点后,就会光栅化这个三角形。光栅化就是所谓的用像素将这个三角形画出来。**对于每一个像素,片元着色器都会询问你这个像素需要使用什么颜色进行上色,你可以通过一个gl_FragColor
**设置当前片元(像素)的颜色。
在上面的片元着色器中,你每次对于gl_FragColor的返回值都是一个固定的向量(颜色),如果你需要多变的颜色,就需要将一些信息(必须顶点的纹理坐标,顶点法线)在片元着色器中进行插值,以确定当前像素的颜色。对于需要从顶点着色器传入片元着色器的变量,一般使用Varying进行申明。
比如将顶点着色器计算出的裁剪空间坐标从顶点着色器传入片元着色器中。
在顶点着色器中定义varying变量传给片元着色器
varying vec4 v_color;
...
void main(void)
{
gl_position = vec4((u_matrix) * vec3(a_position, 1).xy, 0, 1);
.....
v_color = gl_position * 0.5 + 0.5;
}
那么也需要在片元着色器定义同样的Varying变量
#ifdef GL_ES
precision mediump float;
#endif
varying vec4 v_color;
// 在main函数中使用这个颜色值
void main()
{
gl_fragcolor = v_color;
}
我们只计算了三个顶点,调用了三次顶点着色器,按理来说应该也只有三个点的颜色,为什么可以可以绘制出三角形呢?
WebGL先在顶点着色器中计算出了三个顶点的颜色。在光栅化三角形时就会根据三角形的三个顶点信息进行相应的重心坐标插值计算。也就是说,每一个像素在调用片元着色器时,可变的量Varying都是得到插值之后的值!!!
我们从上述的三个顶点开始分析
顶点 | |
---|---|
0 | -100 |
150 | 125 |
-175 | 100 |
我们给三个局部的空间的点进行ModelViewTransform, ProjectionTransform后,将坐标转换到了裁剪空间坐标中,得到在裁剪空间中的值为(也就是写入了gl_position)的信息
顶点 | |
---|---|
0 | 0.660 |
0.750 | -0.830 |
-0.875 | -0.660 |
在顶点着色器中,进行顶点颜色的计算后写入Varying的变量v_color中的值为
写入 v_color 的值 | ||
---|---|---|
0.5000 | 0.830 | 0.5 |
0.8750 | 0.086 | 0.5 |
0.0625 | 0.170 | 0.5 |
利用计算的顶点信息,可以在片元着色器中对每个像素的颜色进行插值
综上,想给片段着色器传值,我们可以先把值传递给顶点着色器进行计算后,在传给片段着色器进行着色。
buffer和attribute的代码是干什么的?
缓冲操作是在GPU上获取顶点和其他顶点数据的一种方式。gl.createBuffer创建了一个缓冲;gl.bindBuffer是设置了缓冲为当前指定的缓冲,gl.bufferData将数据拷贝到了缓冲中,这些操作一般都会在初始化中进行。
一旦数据送入了缓冲中,还需要WebGL如何从缓冲中提取数据,并如何传给顶点着色器!!!!
第一步:需要获取WebGL给属性分配的地址(获得一个指针)这一步通常也会在初始化时完成
var positionLocation = gl.getAttribLocation(program, "a_position")
var colorLocation = gl.getAttribLocation(program, "a_color");
第二步:一旦知道了属性的地址,还要将这个指针启用(也就说告诉WebGL我们想从缓冲中获取这个数据)
gl.enableVertexAttribArray(positionLocation)
gl.enableVertexAttribArray(colorLocation)
我们还需要将创建好的缓冲绑定到一个绑定点上,绑定点充当的角色就是将外部数据送到创建好的缓冲区中
gl.bindBuffer(gl.ARRAY_BUFFER, positon_buffer)
gl.bindBuffer(gl.ARRAY_BUFFER, colot_buffer)
之后需要告诉WebGL去指针对应的地址去获得对应的某些数据
gl.vertexAttribPointer(
location(即之前创建的一个Location指针),
num***ponents,
typeofData,
normalizeFlag,
stride,
offset)
每个数据都使用单独的一个缓冲时,stride和offset都是0,但是如果你想要多个数据贡献一个缓冲,那么就需要自己计算stride和offset的值。
WebGL着色器和GLSL
顶点着色器
一个顶点着色器的主要工作就是生成裁剪空间的坐标值。通常是以下形式
void main()
{
gl_position = MVPTRAMSFORM_TO_GET_CLIP_COORDINATES;
}
每个顶点都需要调用一次顶点着色器,每次调用都需要设置一个特殊的全局变量值gl_position
,该变量就是经过裁剪的空间坐标值
其中
对于顶点着色器所需要的数据,可以通过以下方式获得!!!!
- Attributes属性:从缓冲中获取的数据,也就是每次获取的数据都不同(比如获取的顶点的坐标,顶点的法线)
- Uniforms全局变量:即在一次绘制中,所有的顶点都会进行贡献的值,对所有顶点都是一视同仁的
- Textures纹理:从像素或是纹理元素中获取的值,相当于获取每个顶点的纹理坐标,每次获取的值也都是不一样的
对于Attributes怎么获取呢?
从缓冲中获取
首先需要创建缓冲
var buffer = gl.createBuffer();
之后需要绑定缓冲到缓冲点(ARRAY_BUFFER)
gl.bindBuffer(ARRAY_BUFFER, buffer);
绑定之后就可以向缓冲点写入数据,类比于向缓冲中写入数据
gl.bufferData(gl.ARRAY_BUFFER, somedata, gl.STATIC_DRAW)
获取缓冲的首地址以找到属性所在的地址
var positonLocation = gl.getAttribLocation(ShaderProgra, "a_position")
渲染时告诉WebGL如何从缓冲中取出数据并送入顶点着色器中
// 启用地址
gl.enableVertexAttribArray(positionLocation);
// 从缓冲中读取数据送入顶点着色器程序中
var num***ponents = 3;
var type = gl.FLOAT;
var normalized = false;
var stride = 0;
var offset - 0;
gl.vertexAttribPointer(positionLocation, num***ponents, type, normalized, stride, offset)
在顶点着色器中就可以使用传入的顶点数据进行运算了。下列示例了在vertexshader中进行运算的简单情形
attributes vec4 a_position;
void main()
{
gl_position = a_position;
}
对于uniforms变量如何获取呢
全局变量在每一次顶点计算中都是完全相同的,在下面一个简单例子中,用全局变量给顶点着色器添加了一个偏移量
attribute vec4 a_position;
uniform vec4 u_offset;
void main()
{
gl_positon = a_position + u_offset;
}
如何对全局变量进行初始化呢?
首先在初始化时找到全局变量的地址
var uniformoffsetLocation = gl.getUniformLocation(someProgram, "u_offset")
在渲染绘制之前给全局变量进行赋值
gl.uniform4fv(uniformoffsetLocation, [1, 0, 0, 0])
要注意全局变量是对于单个着色程序的,如果有多个着色程序有同名全局变量,徐娅萍找到每个全局变量并设置自己的值。我们在调用gl.uniform???时,只是设置了当前程序的全局变量
纹理怎么设置呢
同片段着色器中的Texturesw纹理
片段着色器
一个片段着色器的工作是为当前光栅化的像素提供颜色值,通常时以下的形式对当前的像素进行赋值
precision mediump float;
void main()
{
gl_FragColor = Do_Math_To_Get_Color;
}
每个像素都会调用一次片段着色器,每次对于像素的颜色都会从gl_FragColor中获取。
片段着色器的数据,可以从以下途径中获取
- Uniforms全局变量(对于每个像素的着色都是完全相同的)
- Textures纹理
- Varying(从顶点着色器获得并插值到当前像素)
Textures纹理怎么获取?
在着色器中获取纹理信息,可以先创建一个sampler2D类型的全局变量,然后使用
texture2D方法,从纹理中获取信息,如下
precision mediump float;
uniform sampler2D u_texture;
void main()
{
// 需要获取纹理值的坐标,纹理坐标
vec2 texcoords = vec2(0.5, 0.5);
gl_FragColor = texture2D(u_texture, texcoords);
}
那么如何向uniform sampler2D 变量中写入全局变量的数据呢?
这就是一件很复杂的事,之后在开一篇。但至少要创建并给这个纹理数据填充数据。例如
// 创建一个纹理
var tex = gl.createTexture();
// 绑定纹理到一个纹理的绑定点
gl.bindTexture(gl.TEXTURE2D, tex);
var level = 0;
var width = 2;
var height = 1;
var data = new int3Array([
255, 0, 0, 255,
0, 255, 0, 255,
])
gl.texImage(gl.TEXTURE_2D, level, gl.RGBA, wigth, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, data);
// 设置纹理的参数
gl.texParameteri(gl.TEXTURE2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
在初始化全局变量时找到纹理的地址
var someSampleLocation = gl.getUniformLocation(someProgram, 'u_texture');
在渲染时,WebGL要求纹理必须绑定到一个纹理单元上
// 指定纹理单元
var unit = 5;
gl.activeTexture(gl.TEXTURE0 + unit);
gl.bindTexture(gl.TEXTURE2D, tex);
然后告诉着色器你要使用的纹理在那个纹理单元里
gl.uniform1i(someSampleLocation, unit);
Varying变量
Varying变量就是之前说过的,通过顶点着色器传给片元着色器后,可以进行插值计算
https://webglfundamentals.org/webgl/lessons/zh_***/webgl-how-it-works.html