理论基础:
顶点着色器 Vertex Shader
主要是负责处理顶点位置、顶点颜色、顶点向量等顶点的数据;处理一些顶点的变换:例如在进行视图变换和投影变换时MVP矩阵会改变顶点的位置信息。
输入:
顶点着色器输入部分主要是声明:
①储存着每个顶点属性的attribute变量;
②所有顶点都会实现的(例如MVP矩阵、透视变换矩阵等)全局的uniform变量;
③一些会传递给片元着色器的varying变量。
// 变量
attribute vwc3 aVertexPosition; // 顶点位置
attribute vec3 aNormalPosition; // 法线
attribute vec2 aTextureCoord; // uv坐标
varying highp vec2 vTextureCoord; // 顶点着色器传递给片元着色器
varying highp vec3 vFragPos;
varying highp vec3 vNormal;
// 矩阵
uniform mat4 uModelViewMatrix; // 视图变换矩阵
uniform mat4 uProjectionMatrix; // 透视变换矩阵
输出:
①顶点坐标gl_Position;
②给输入部分声明的varying变量赋值;
矩阵运算从右往左运算
vFragPos = aVertexPosition;
vNormal = aNormalPosition;
// 计算点坐标
gl_Position = uProjextionMatrix * uModelViewMatrix * vec4(aVertexPosition,1.0);
vTextureCoord = aTextureCoord;
片元着色器 Fragment Shader
用于颜色、贴图、光照阴影等
//除了上述出现的“着色器不支持高精度,限定浮点类型为中精度”
precision mediump float;
//还有表示支持高精度的:“着色器支持高精度,限定浮点类型为高精度”
precision highp float;
//以及仅支持低精度的:
prcision lowp float;
输入:
uniform声明
/声明一个纹理相关的变量,sampler2D 也是一种数据类型
//它是一种取样器类型的变量,该变量对应传入的纹理2D图片的像素数据
uniform sampler2D uSampler;
//Blinn-Phong需要的漫反射项kd、高光项ks、光源位置、相机位置、光强
uniform vec3 uKd;
uniform vec3 uKs;
uniform vec3 uLightPos;
uniform vec3 uCameraPos;
uniform float uLightIntensity;
// 这个TextureSample参数作用应该是:
// 以左上角为原点,通过UV截取图片,如果==1,则正常取值
uniform int uTextureSample;
varing变量
//继承varying参数
varying highp vec2 vTextureCoord;
varying highp vec3 vFragPos;
varying highp vec3 vNormal;
输出:
一般设置为gl_FragColor
参考:GAMES101-现代计算机图形学入门-闫令琪_哔哩哔哩_bilibili
gl.compileShader(shader), 传递需要编译的着色器,函数实现着色器编译。
gl.getShaderParameter(shader,COMPILE_STATUS),COMPILE_STATUS为:
gl.DELETE_STATUS
:标示着色器是否被删除,删除(GL_TRUE)未删除(GL_FALSE).gl.COMPILE_STATUS
: 标示着色器是否编译成功,是(GL_TRUE)不是(GL_FALSE)gl.SHADER_TYPE
: 标示着色器类型,是顶点着色器 (gl.VERTEX_SHADER
) 还是片段着色器 (gl.FRAGMENT_SHADER
)
WebGLRenderingContext.enableVertexAttribArray()
在 WebGL 中,作用于顶点的数据会先储存在attributes。这些数据仅对 JavaScript 代码和顶点着色器可用。属性由索引号引用到 GPU 维护的属性列表中。在不同的平台或 GPU 上,某些顶点属性索引可能具有预定义的值。创建属性时,WebGL 层会分配其他属性。
类型为GLuint 的索引,指向要激活的顶点属性。可以使用getAttribLocation方法来获取索引.
WebGLRenderingContext.vertexAttribPointer(index,size,type,normalized,stride,offset)
打开属性数组列表中指定索引处的通用顶点属性数组,
index:定要修改的顶点属性的索引。
size:指定每个顶点属性的组成数量,必须是 1,2,3 或 4。
type:
指定数组中每个元素的数据类型可能是:
-
gl.BYTE
: signed 8-bit integer, with values in [-128, 127] 有符号的 8 位整数,范围 [-128, 127] -
gl.SHORT
: signed 16-bit integer, with values in [-32768, 32767] 有符号的 16 位整数,范围 [-32768, 32767] -
gl.UNSIGNED_BYTE
: unsigned 8-bit integer, with values in [0, 255] 无符号的 8 位整数,范围 [0, 255] -
gl.UNSIGNED_SHORT
: unsigned 16-bit integer, with values in [0, 65535] 无符号的 16 位整数,范围 [0, 65535] -
gl.FLOAT
: 32-bit IEEE floating point number 32 位 IEEE 标准的浮点数 -
When using a WebGL 2 context, the following values are available additionally: 使用 WebGL2 版本的还可以使用以下值:
gl.HALF_FLOAT
: 16-bit IEEE floating point number 16 位 IEEE 标准的浮点数
normalize:
当转换为浮点数时是否应该将整数数值归一化到特定的范围。
- or types
gl.BYTE
andgl.SHORT
, normalizes the values to [-1, 1] if true. 对于类型gl.BYTE
和gl.SHORT
,如果是 true 则将值归一化为 [-1, 1] - For types
gl.UNSIGNED_BYTE
andgl.UNSIGNED_SHORT
, normalizes the values to [0, 1] if true. 对于类型gl.UNSIGNED_BYTE
和gl.UNSIGNED_SHORT
,如果是 true 则将值归一化为 [0, 1] - For types
gl.FLOAT
andgl.HALF_FLOAT
, this parameter has no effect. 对于类型gl.FLOAT
和gl.HALF_FLOAT
,此参数无效
stride:
一个 GLsizei,以字节为单位指定连续顶点属性开始之间的偏移量 (即数组中一行长度)。不能大于 255。如果 stride 为 0,则假定该属性是紧密打包的,即不交错属性,每个属性在一个单独的块中,下一个顶点的属性紧跟当前顶点之后。
offset:
指定顶点属性数组中第一部分的字节偏移量。必须是类型的字节长度的倍数。
WebGLRenderingContext.uniformMatrix[234]fv(location,transpose,value)
该方法的 3 个版本(uniformMatrix2fv()
、uniformMatrix3fv()
和 unifomMatrix4fv()
),分别以二阶、三阶和四阶方阵作为输入值,它们应是分别具有 4、9、16 个浮点数的数组。
WebGLRenderingContext.uniformMatrix2fv(location, transpose, value);
WebGLRenderingContext.uniformMatrix3fv(location, transpose, value);
WebGLRenderingContext.uniformMatrix4fv(location, transpose, value);
location
WebGLUniformLocation 对象包含了要修改的 uniform attribute 位置。位置使用 getUniformLocation()获得。
transpose
GLboolean 指定是否转置矩阵。必须为 false
。
value
Float32Array 型或者是 GLfloat
序列值。这些值被假定按列主序(column major order)的方式提供。
源码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="gl-matrix.js"></script>
<script>
let vertexstring = `
attribute vec4 a_position;
uniform mat4 u_formMatrix;
uniform mat4 proj;
attribute vec4 a_color;
varying vec4 color;
void main(void){
gl_Position = u_formMatrix * a_position;
color = a_color;
} `;
let fragmentstring = `
precision mediump float;
varying vec4 color;
void main(void){
gl_FragColor =color;
}
`;
var webgl;
var near=0;
var far= 50;
function init() {
initWebgl();
initShader();
initBuffer();
draw();
inittext(near,far)
initEvent();
}
function inittext(near,far){
document.getElementById("text").innerHTML = "near:"+near +"<br/>"+ "far:"+far;
}
function initEvent() {
document.onkeydown = handleKeyDown;
}
function handleKeyDown(event) {
if (String.fromCharCode(event.keyCode) == 'W') {
near += 1;
}
else if (String.fromCharCode(event.keyCode) == 'S') {
near -=1;
}
else if (String.fromCharCode(event.keyCode) == 'A') {
far -= 1;
}
else if (String.fromCharCode(event.keyCode) == 'D') {
far += 1;
}
inittext(near,far)
initBuffer();
draw();
}
function initWebgl() {
let webglDiv = document.getElementById('myCanvas');
webgl = webglDiv.getContext("webgl");
webgl.viewport(0, 0, webglDiv.clientWidth, webglDiv.clientHeight);
}
function initShader() {
let vsshader = webgl.createShader(webgl.VERTEX_SHADER);
let fsshader = webgl.createShader(webgl.FRAGMENT_SHADER);
webgl.shaderSource(vsshader, vertexstring);
webgl.shaderSource(fsshader, fragmentstring);
webgl.compileShader(vsshader);
webgl.compileShader(fsshader);
if (!webgl.getShaderParameter(vsshader, webgl.COMPILE_STATUS)) {
var err = webgl.getShaderInfoLog(vsshader);
alert(err);
return;
}
if (!webgl.getShaderParameter(fsshader, webgl.COMPILE_STATUS)) {
var err = webgl.getShaderInfoLog(fsshader);
alert(err);
return;
}
let program = webgl.createProgram();
webgl.attachShader(program, vsshader);
webgl.attachShader(program, fsshader)
webgl.linkProgram(program);
webgl.useProgram(program);
webgl.program = program
}
function initBuffer() {
let ProjMatrix = glMatrix.mat4.create();
glMatrix.mat4.identity(ProjMatrix);
glMatrix.mat4.ortho(ProjMatrix,-100,100,-100,100,near,far) //修改可视域范围
let arr = [
0.0, 70, -40, 1, 1, 0, 0, 1,
-50, -30, -40, 1, 1, 0, 0, 1, // 绿色
50, -30, -40, 1, 1, 0, 0, 1,
50, 40, -20, 1, 1.0, 1.0, 0.4, 1,
-50, 40, -20, 1, 1.0, 1.0, 0.4, 1,
0.0, -60,-20, 1, 1.0, 1.0, 0.4, 1,// 黄色
0.0, 50, 0.0, 1, 0.4, 0.4, 1.0, 1,
-50, -50, 0.0, 1, 0.4, 0.4, 1.0, 1,
50, -50, 0.0, 1, 0.4, 0.4, 1.0, 1, // 蓝色
]
let pointPosition = new Float32Array(arr);
let aPsotion = webgl.getAttribLocation(webgl.program, "a_position");
let triangleBuffer = webgl.createBuffer();
webgl.bindBuffer(webgl.ARRAY_BUFFER, triangleBuffer);
webgl.bufferData(webgl.ARRAY_BUFFER, pointPosition, webgl.STATIC_DRAW);
webgl.enableVertexAttribArray(aPsotion);
webgl.vertexAttribPointer(aPsotion, 4, webgl.FLOAT, false, 8 * 4, 0);
let aColor = webgl.getAttribLocation(webgl.program, "a_color");
webgl.enableVertexAttribArray(aColor);
webgl.vertexAttribPointer(aColor, 4, webgl.FLOAT, false, 8 * 4, 4 * 4);
let uniformMatrix1 = webgl.getUniformLocation(webgl.program, "u_formMatrix");
webgl.uniformMatrix4fv(uniformMatrix1, false, ProjMatrix)
}
function draw() {
webgl.clearColor(0.0, 1.0, 0.0, 1.0);
webgl.clear(webgl.COLOR_BUFFER_BIT);
webgl.drawArrays(webgl.TRIANGLES, 0, 9);
}
</script>
</head>
<body onload="init()">
<canvas id='myCanvas' width="1024" height='768'></canvas>
<div id="text"></div>
</body>
</html>
效果展示:
可操作键盘方向键进行显示变换。