WebGL 读书笔记 vertax,fragment
1. 基础
WebGL中有两种基本着色器:
顶点着色器
- 顶点着色器用来描述顶点特性,如位置、颜色。顶点是指二维或者三维空间中的一个点,比如二维或者三位图形的端点或交点。
- 主要用来控制点的位置和大小
GL内置变量
vec4 gl_Position
表示顶点位置float gl_PointSize
表示点的尺寸(像素数)
片元着色器(也称片段着色器)
- 可以理解为,为顶点着色器定义出来的区域进行颜色渲染填充。如果没有定义片元着色器,那么将由GPU自行进行差值填充
内置变量
vec4 gl_FragColor
指定片元颜色,RGBA格式。
- 归一化
绘制流程: 顶点着色器标记坐标-> 片元着色器标记颜色-> gl.drawArrays进行渲染
1.1 内部变量
1.1.1 attribute(存储限定符
) 声明变量
attribute
与uniform
都是用来定义openGL中可与JS进行通讯交互的变量- 但是
attribute
被JS用来传输与顶点
相关的数据。 uniform
变量传输的是对于所有顶点相同的数据或者与顶点无关的数据。WebGL函数命名遵循
<基本函数名><参数个数><参数类型>
进行。vertexArrtib1f
传递1个值vertexArrtib2f
传递2个值vertexArrtib3f
传递3个值vertexArrtib4f
传递4个值f
表示浮点数i
表示整数
"use strict";
var vertexShaderSource = `#version 300 es
// 1. 在顶点着色器中声明attribute变量
// an attribute is an input (in) to a vertex shader.
// It will receive data from a buffer
in vec4 a_position;
// all shaders have a main function
void main() {
// 2. 将attribute变量赋值给gl_Position
// gl_Position is a special variable a vertex shader
// is responsible for setting
gl_Position = a_position;
}
`;
var fragmentShaderSource = `#version 300 es
// fragment shaders don't have a default precision so we need
// to pick one. highp is a good default. It means "high precision"
precision highp float;
// we need to declare an output for the fragment shader
out vec4 outColor;
void main() {
// Just set the output to a constant redish-purple
outColor = vec4(1, 0, 0.5, 1);
}
`;
function createShader(gl, type, source) {
var shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (success) {
return shader;
}
console.log(gl.getShaderInfoLog(shader)); // eslint-disable-line
gl.deleteShader(shader);
return undefined;
}
function createProgram(gl, vertexShader, fragmentShader) {
var program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
var success = gl.getProgramParameter(program, gl.LINK_STATUS);
if (success) {
return program;
}
console.log(gl.getProgramInfoLog(program)); // eslint-disable-line
gl.deleteProgram(program);
return undefined;
}
function main() {
// Get A WebGL context
var canvas = document.querySelector("#c");
var gl = canvas.getContext("webgl2");
if (!gl) {
return;
}
// create GLSL shaders, upload the GLSL source, compile the shaders
var vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
var fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
// Link the two shaders into a program
var program = createProgram(gl, vertexShader, fragmentShader);
// look up where the vertex data needs to go.
// 3. 获取attribute变量存储位置
var positionAttributeLocation = gl.getAttribLocation(program, "a_position");
// Create a buffer and put three 2d clip space points in it
var positionBuffer = gl.createBuffer();
// Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)
// 4. 将JS中的顶点位置坐标信息传输给webgl
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
var positions = [
0, 0,
0, 0.5,
0.7, 0,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
// Create a vertex array object (attribute state)
var vao = gl.createVertexArray();
// and make it the one we're currently working with
gl.bindVertexArray(vao);
// Turn on the attribute
gl.enableVertexAttribArray(positionAttributeLocation);
// Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
var size = 2; // 2 components 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);
webglUtils.resizeCanvasToDisplaySize(gl.canvas);
// Tell WebGL how to convert from clip space to pixels
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);
// Tell it to use our program (pair of shaders)
gl.useProgram(program);
// Bind the attribute/buffer set we want.
gl.bindVertexArray(vao);
// draw
var primitiveType = gl.TRIANGLES;
var offset = 0;
var count = 3;
gl.drawArrays(primitiveType, offset, count);
}
main();
1.1.2 uniform 声明变量
- 用来传输那些与顶点相关或者其他无关的数据。
- js使用
gl.uniform4f()
向webgl传递数据,并且4f的格式
规范跟之前的定义相同。
1.1.3 varying 声明变量
varying
是用于vertex(顶点着色器)
和fragment shader(片元着色器)
之间做数据传递用的。- 如果顶点着色器中有类型和命名相同的
varying
变量,那么顶点着色器赋给该变量的值就会被自动传入片元着色器。 - 一般
vertex shader
修改varying
变量的值,然后fragment shader
使用该varying
变量的值。 - 因此
varying
变量在vertex
和fragment shader
二者之间的声明必须是一致的。application不能使用此变量。
let VSHADER_SOURCE =
"attribute vec4 a_Position;\n" +
"attribute float a_PointSize;\n" +
"attribute vec4 a_Color;\n" +
"varying vec4 v_Color;\n" +
// varying 声明
"uniform mat4 u_ModelMatrix;\n" +
"void main() {\n" +
" gl_Position = u_ModelMatrix * a_Position;\n" +
" gl_PointSize = a_PointSize;\n" +
" v_Color = a_Color;\n" +
// 将颜色赋值给v_Color,用于传递给片元着色器
"}\n"
let FSHADER_SOURCE =
"precision mediump float;\n" +
"uniform vec4 u_FragColor;\n" +
"varying vec4 v_Color;\n" +
// 从顶点着色器传送过来的数据 恢复v_Color数据
// 变量名称必须一致
"void main() {\n" +
" gl_FragColor = v_Color;\n" +
"}\n",
varying变量的作用和内插过程
varying变量。只能是float、vec2、vec3、vec4、mat2、mat3、mat4。必须全局。最少支持8个。
顶点着色器中的v_Color
在传入片元着色器之前经过了内插过程。WebGL根据我们传入的颜色值,自动计算出所有片元的颜色,赋值给片元着色器中的v_Color
。
图元装配到光栅化
- 缓冲区对象中第一个坐标传递给
a_Position
继而被赋值给gl_Position
,赋值后改顶点进入图形装配区,并储存。 - 重复执行顶点着色器,将所有坐标点传入并存储在装配区。
- 装配图形。根据
gl.drawArrays()
第一个参数决定如何装配顶点。 - 光栅化。将装配好的图元转化为片元(像素)。
- 光栅化结束后,逐片元调用片元着色器。每次处理一个片元,计算出该片元颜色,写入颜色缓冲区。直到所有片元被处理完,浏览器显示出结果。