理论基础
Games101(通常指的是一门广受欢迎的计算机图形学入门课程)的内容大纲广泛而深入,涵盖了计算机图形学的多个核心领域。以下是一个概括性的内容大纲,旨在提供对课程主要内容的概览:
一、计算机图形学基础
- 概述:介绍计算机图形学的基本概念、应用领域(如3D动画、游戏、AR/VR/XR、物理模拟、UI等)以及它与其他学科(如数学、物理)的关系。
- 数学基础:线性代数(向量、矩阵、变换等)、微积分(用于光照、阴影等计算)等数学工具在图形学中的应用。
二、图形学基本技术
- 光栅化:将3D物体投影到屏幕上的过程,包括投影转换、视图转换、模型转换等,以及如何将投影结果转换为像素并实时渲染。
- 曲线与曲面:介绍如何表示和渲染曲线(如贝塞尔曲线)和曲面(如NURBS曲面),以及如何通过细分等方法得到更复杂的曲面。
- 光线追踪:从相机发射光线穿过每个像素,计算光线与场景的交集、阴影,并继续反射光线直到击中光源,以实现更为真实的光照效果。
三、物理模拟与动画
- 物理光照与阴影:讨论光照模型(如泛光模型、Lambert漫反射模型、Phong反射模型等)以及阴影的生成方法。
- 动画与模拟:介绍动画的基本原理和技术,包括关键帧动画、骨骼动画等,以及如何通过物理模拟来模拟真实世界的物理场景。
四、图形学进阶主题
- 纹理映射:包括法线贴图、凹凸贴图、位移贴图和环境贴图等,用于增加模型的细节和真实感。
- 抗锯齿与深度测试:介绍抗锯齿算法(如MSAA、FXAA、TAA等)和Z-Buffer算法,以解决渲染过程中的锯齿和深度测试问题。
- 渲染优化:讨论如何优化渲染过程,以提高渲染效率和质量,包括层次细节(LOD)技术、光照贴图等。
五、实战与项目
- 课程项目:通过实际项目来巩固和应用所学知识,如实现一个简单的3D渲染器、动画系统或物理模拟系统。
- 工具与软件:介绍常用的图形学工具和软件,如VS或VSC等,并指导学生如何使用这些工具进行图形学开发。
请注意,以上大纲仅为Games101课程可能包含内容的概览,具体课程内容可能会因教师、学期或学校而异。因此,建议直接参考相关课程网站或教材以获取最准确和详细的信息。
在正式进入webgl之前,我想有必要简单了解一下渲染管线,毕竟它贯穿webgl学习的整个过程。
渲染管线流程图:
编辑
除此之外,需简单了解一下webgl着色器简单语法:
向量与矩阵 | 常见内置变量 | 常见数据类型 | 常见修饰符 |
vec3 | gl_PointSize | float | attribute |
vec4 | gl_Position | int | uniform |
mat3 | gl_FragColor | bool | varying |
mat4 | gl_FragCoord | sampler2D | — |
最后在正式开始前,先大致了解整个流程:
编辑
编辑
编辑
(注:上述内容为搭建一个框架,此外课程学习前可能需一些计算机图形学和线性代数的基本知识,若还未学习相关内容的小伙伴可以去B站学习相关内容,推荐《计算机图形学入门》和《线性代数的本质》两门课程,当然学习webgl还是很推荐郭老师的课程哈!课程+博客!)
编辑
编辑
编辑
编辑
编辑
项目案例
绘制一个钟
2.实现代码:
<!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>时针</title>
<style>
#myCanvas{
background-color: rgb(246, 246, 249);
}
</style>
<script src="glMatrix-0.9.6.min.js"></script>
<script>
let vertexstring = `
attribute vec4 a_position;
uniform mat4 u_formMatrix;
void main(void){
gl_Position =u_formMatrix * a_position;
}
`;
let fragmentstring = `
precision mediump float;
void main(void){
gl_FragColor =vec4(0.0,0.0,0.0,1.0);
}
`;
var projMat4 = mat4.create();
var webgl;
var uniformTexture = 0;
var uniformTexture1 = 0;
var uniformAnim = 0;
var count = 0;
function webglStart() {
init();
tick();
}
function tick() {
window.setTimeout(tick, 1000);
initbuffer(0.8);
initbuffer(0.5);
initbuffer(0.4);
count = count +1;
};
function init() {
initWebgl();
initShader();
}
function initWebgl() {
let webglDiv = document.getElementById('myCanvas');
webgl = webglDiv.getContext("webgl");
webgl.viewport(0, 0, webglDiv.clientWidth, webglDiv.clientHeight);
//mat4.ortho(0, webglDiv.clientWidth, webglDiv.clientHeight, 0, -1.0, 1.0, projMat4)
}
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(radius){
//秒分时针,根据长度分且角度需合理设置
let angle;
if(radius>0.6){
//角度为负值
angle = -Math.PI/30 *count;
}
else if(radius>0.4){
angle = -Math.PI/1800 *count;
}
else{
angle = -Math.PI/1800 *count *5/60;
// angle = -Math.PI/30 *count;
}
const cos = Math.cos(angle)*radius;
const sin = Math.sin(angle)*radius;
var matArr= new Float32Array([
cos,sin,0,0,
-sin,cos,0,0,
0,0,1,0,
0,0,0,1
]);
let arr = [
0, 0, 0, 1,
0, 0.05, 0, 1,
radius, 0, 0, 1,
radius, 0, 0, 1,
0, 0.05, 0, 1,
radius, 0.05, 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, 0, 0);
let uniformMatrix = webgl.getUniformLocation(webgl.program, "u_formMatrix");
webgl.uniformMatrix4fv(uniformMatrix, false, matArr)
webgl.drawArrays(webgl.TRIANGLES, 0, 6);
}
3.核心讲解:
3.1动画设置:每隔1秒钟绘制新的图形(图形不断进行旋转)
function tick() {
window.setTimeout(tick, 1000);
initbuffer(0.8);
initbuffer(0.5);
initbuffer(0.4);
count = count +1;
};
3.2时针旋转角度计算、旋转矩阵构建:根据常识计算旋转角度,需要注意的是这角度为负值,旋转方向才为顺时针,旋转矩阵可看作绕Z轴旋转所得的矩阵
//秒分时针,根据长度分且角度需合理设置
let angle;
if(radius>0.6){
//角度为负值
angle = -Math.PI/30 *count;
}
else if(radius>0.4){
angle = -Math.PI/1800 *count;
}
else{
angle = -Math.PI/1800 *count *5/60;
// angle = -Math.PI/30 *count;
}
const cos = Math.cos(angle)*radius;
const sin = Math.sin(angle)*radius;
var matArr= new Float32Array([
cos,sin,0,0,
-sin,cos,0,0,
0,0,1,0,
0,0,0,1
]);
MVP变换+层次模型+键盘事件来个交互的小机器人
2.实现代码:
<!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>
<style>
#myCanvas{
background-color: antiquewhite;
}
</style>
<script src="gl-matrix.js"></script>
<script>
let vertexstring = `
attribute vec4 a_position;
uniform mat4 u_formMatrix;
attribute vec4 a_Normal;
uniform vec3 u_PointLightPosition;
uniform vec3 u_DiffuseLight;
uniform vec3 u_AmbientLight;
varying vec4 v_Color;
void main(void){
gl_Position = u_formMatrix * a_position;
vec3 normal = normalize(a_Normal.xyz);
vec3 lightDirection = normalize(u_PointLightPosition - vec3(gl_Position.xyz));
float nDotL = max(dot(lightDirection, normal), 0.0);
vec3 diffuse = u_DiffuseLight * vec3(1.0,0,1.0)* nDotL;
vec3 ambient = u_AmbientLight * vec3(1.0,0,1.0);
v_Color = vec4(diffuse + ambient, 1);
}
`;
let fragmentstring = `
precision mediump float;
varying vec4 v_Color;
void main(void){
gl_FragColor =v_Color;
}
`;
var webgl;
var angle = 45;
var webglDiv;
var indices
var g_joint1Angle = 86.0;
var ANGLE_STEP = 3.0;
var g_arm1Angle = 160.0;
var g_palm1Angle = 0.0;
var g_finger1Angle = 0.0;
var g_chest1Angle = 0
function init() {
//初始化webgl
initWebgl();
//初始化shader
initShader();
clearn();
initLight();
drawHead()
//初始化数据
initBuffer();
//初始化事件
initEvent();
//清空画板
//创建光源
//绘制图形
let drawMatrix = chestDraw();
let drawMatrixCopy = drawMatrix.slice(0);
drawLeft(drawMatrix);
drawRight(drawMatrixCopy);
}
function initWebgl() {
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 clearn() {
webgl.clearColor(0, 0, 0, 1);
webgl.clear(webgl.COLOR_BUFFER_BIT | webgl.DEPTH_BUFFER_BIT);
webgl.enable(webgl.DEPTH_TEST);
}
//矩阵变换 g_joint1Angle
function initTransformation(angele, rotateArr, ModelMatrix = glMatrix.mat4.create()) {
let ProjMatrix = glMatrix.mat4.create();
glMatrix.mat4.identity(ProjMatrix);
glMatrix.mat4.perspective(ProjMatrix, angle * Math.PI / 180, webglDiv.clientWidth / webglDiv.clientHeight, 1, 1000) //修改可视域范围
let uniformMatrix1 = webgl.getUniformLocation(webgl.program, "u_formMatrix");
glMatrix.mat4.rotate(ModelMatrix, ModelMatrix, degreesToRads(angele), rotateArr);
let ViewMatrix = glMatrix.mat4.create();
glMatrix.mat4.identity(ViewMatrix);
glMatrix.mat4.lookAt(ViewMatrix, [0, 0, 100], [0, 0, 0], [0, 1, 0]);
let mvMatrix = glMatrix.mat4.create();
glMatrix.mat4.identity(mvMatrix);
glMatrix.mat4.multiply(mvMatrix, ViewMatrix, ModelMatrix);
let mvpMatrix = glMatrix.mat4.create();
glMatrix.mat4.identity(mvpMatrix);
glMatrix.mat4.multiply(mvpMatrix, ProjMatrix, mvMatrix);
webgl.uniformMatrix4fv(uniformMatrix1, false, mvpMatrix)
return ModelMatrix;
}
function initEvent() {
document.onkeydown = keydown;
}
function initLight() {
let u_DiffuseLight = webgl.getUniformLocation(webgl.program, 'u_DiffuseLight');
webgl.uniform3f(u_DiffuseLight, 1.0, 1.0, 1.0);
let u_LightDirection = webgl.getUniformLocation(webgl.program, 'u_PointLightPosition');
webgl.uniform3fv(u_LightDirection, [3.0, 3.0, 4.0]);
let u_AmbientLight = webgl.getUniformLocation(webgl.program, 'u_AmbientLight');
webgl.uniform3f(u_AmbientLight, 0.8, 0.8, 0.8);
}
function initData() {
}
function initBuffer() {
var vertices = new Float32Array([
1.5, 10.0, 1.5, -1.5, 10.0, 1.5, -1.5, 0.0, 1.5, 1.5, 0.0, 1.5, // v0-v1-v2-v3 front
1.5, 10.0, 1.5, 1.5, 0.0, 1.5, 1.5, 0.0, -1.5, 1.5, 10.0, -1.5, // v0-v3-v4-v5 right
1.5, 10.0, 1.5, 1.5, 10.0, -1.5, -1.5, 10.0, -1.5, -1.5, 10.0, 1.5, // v0-v5-v6-v1 up
-1.5, 10.0, 1.5, -1.5, 10.0, -1.5, -1.5, 0.0, -1.5, -1.5, 0.0, 1.5, // v1-v6-v7-v2 left
-1.5, 0.0, -1.5, 1.5, 0.0, -1.5, 1.5, 0.0, 1.5, -1.5, 0.0, 1.5, // v7-v4-v3-v2 down
1.5, 0.0, -1.5, -1.5, 0.0, -1.5, -1.5, 10.0, -1.5, 1.5, 10.0, -1.5 // v4-v7-v6-v5 back
]);
// Normal
var normals = new Float32Array([
0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, // v0-v1-v2-v3 front
1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, // v0-v3-v4-v5 right
0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, // v0-v5-v6-v1 up
-1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, // v1-v6-v7-v2 left
0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, // v7-v4-v3-v2 down
0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0 // v4-v7-v6-v5 back
]);
// Indices of the vertices
indices = new Uint8Array([
0, 1, 2, 0, 2, 3, // front
4, 5, 6, 4, 6, 7, // right
8, 9, 10, 8, 10, 11, // up
12, 13, 14, 12, 14, 15, // left
16, 17, 18, 16, 18, 19, // down
20, 21, 22, 20, 22, 23 // back
]);
let pointPosition = new Float32Array(vertices);
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, 3, webgl.FLOAT, false, 0, 0);
let aNormal = webgl.getAttribLocation(webgl.program, "a_Normal");
let normalsBuffer = webgl.createBuffer();
let normalsArr = new Float32Array(normals);
webgl.bindBuffer(webgl.ARRAY_BUFFER, normalsBuffer);
webgl.bufferData(webgl.ARRAY_BUFFER, normalsArr, webgl.STATIC_DRAW);
webgl.enableVertexAttribArray(aNormal);
webgl.vertexAttribPointer(aNormal, 3, webgl.FLOAT, false, 0, 0);
let indexBuffer = webgl.createBuffer();
let indices1 = new Uint8Array(indices);
webgl.bindBuffer(webgl.ELEMENT_ARRAY_BUFFER, indexBuffer);
webgl.bufferData(webgl.ELEMENT_ARRAY_BUFFER, indices1, webgl.STATIC_DRAW);
}
function keydown(ev) {
console.log(g_joint1Angle);
debugger
switch (ev.keyCode) {
case 38:
if (g_joint1Angle < 135.0) g_joint1Angle += ANGLE_STEP;
break;
case 40:
if (g_joint1Angle > -135.0) g_joint1Angle -= ANGLE_STEP;
break;
case 39:
g_arm1Angle += ANGLE_STEP;
break;
case 37:
g_arm1Angle -= ANGLE_STEP;
break;
case 87:
g_palm1Angle += ANGLE_STEP;
break;
case 83:
g_palm1Angle -= ANGLE_STEP;
break;
case 90:
g_finger1Angle += ANGLE_STEP;
break;
case 79:
g_chest1Angle += ANGLE_STEP;
break;
case 80:
g_chest1Angle -= ANGLE_STEP;
break;
default: return;
}
clearn()
initBuffer();
let drawMatrix = chestDraw();
let drawMatrixCopy = drawMatrix.slice(0)
drawLeft(drawMatrix);
drawRight(drawMatrixCopy);
drawHead()
}
function degreesToRads(deg) {
let rads = (deg * Math.PI) / 180.0;
return rads;
}
function chestDraw() {
let chestPosition = glMatrix.mat4.create();
glMatrix.mat4.translate(chestPosition, chestPosition, [14.5, 0, 0]);
glMatrix.mat4.scale(chestPosition, chestPosition, [8, 1.5, 2]);
let modelArr = initTransformation(g_chest1Angle, [0, 1, 0], chestPosition);//return ModelMatrix
webgl.drawElements(webgl.TRIANGLES, indices.length, webgl.UNSIGNED_BYTE, 0);
return modelArr;
}
function drawLeft(BigArmPosition) {
//绘制大手臂
// let BigArmPosition = glMatrix.mat4.create();
glMatrix.mat4.translate(BigArmPosition, BigArmPosition, [-1.5, 0, 0]);
glMatrix.mat4.scale(BigArmPosition, BigArmPosition, [3 / 16, 1 / 1.5, 3 / 4]);
// glMatrix.mat4.scale(BigArmPosition, BigArmPosition, [1.5, 1, 1.5]);
let modelArr = initTransformation(g_joint1Angle, [0, 1, 0], BigArmPosition);
webgl.drawElements(webgl.TRIANGLES, indices.length, webgl.UNSIGNED_BYTE, 0);
//绘制小手臂
let armPosition = glMatrix.mat4.create();
glMatrix.mat4.identity(armPosition);
glMatrix.mat4.translate(armPosition, modelArr, [0, 0, 0]);
glMatrix.mat4.scale(armPosition, armPosition, [0.8, 1, 0.8]);
let mvpArr = initTransformation(g_arm1Angle, [0, 0, 1], armPosition);
webgl.drawElements(webgl.TRIANGLES, indices.length, webgl.UNSIGNED_BYTE, 0);
//绘制手掌
glMatrix.mat4.translate(mvpArr, mvpArr, [0, 10, 0]);
glMatrix.mat4.scale(mvpArr, mvpArr, [2, 0.2, 2]);
mvpArr = initTransformation(g_palm1Angle, [0, 1, 0], mvpArr);
webgl.drawElements(webgl.TRIANGLES, indices.length, webgl.UNSIGNED_BYTE, 0);
//绘制手指
let fingerPosition1 = glMatrix.mat4.create();
glMatrix.mat4.identity(fingerPosition1);
glMatrix.mat4.translate(fingerPosition1, mvpArr, [1, 10, 0]);
glMatrix.mat4.scale(fingerPosition1, fingerPosition1, [0.2, 5, 0.2]);
fingerPosition1 = initTransformation(g_finger1Angle, [0, 0, 1], fingerPosition1);
webgl.drawElements(webgl.TRIANGLES, indices.length, webgl.UNSIGNED_BYTE, 0);
let fingerPosition2 = glMatrix.mat4.create();
glMatrix.mat4.identity(fingerPosition2);
glMatrix.mat4.translate(fingerPosition2, mvpArr, [-1, 10, 0]);
glMatrix.mat4.scale(fingerPosition2, fingerPosition2, [0.2, 5, 0.2]);
mvpArr = initTransformation(g_finger1Angle, [0, 0, 1], fingerPosition2);
webgl.drawElements(webgl.TRIANGLES, indices.length, webgl.UNSIGNED_BYTE, 0);
}
function drawRight(BigArmPosition) {
//绘制大手臂
// let BigArmPosition = glMatrix.mat4.create();
glMatrix.mat4.translate(BigArmPosition, BigArmPosition, [1.5, 0, 0]);
glMatrix.mat4.scale(BigArmPosition, BigArmPosition, [1 / 8, 1 / 1.5, 1 / 2]);
glMatrix.mat4.scale(BigArmPosition, BigArmPosition, [1.5, 1, 1.5]);
let modelArr = initTransformation(g_joint1Angle, [0, 1, 0], BigArmPosition);
webgl.drawElements(webgl.TRIANGLES, indices.length, webgl.UNSIGNED_BYTE, 0);
//绘制小手臂
let armPosition = glMatrix.mat4.create();
glMatrix.mat4.identity(armPosition);
glMatrix.mat4.translate(armPosition, modelArr, [0, 0, 0]);
glMatrix.mat4.scale(armPosition, armPosition, [0.8, 1, 0.8]);
let mvpArr = initTransformation(g_arm1Angle, [0, 0, 1], armPosition);
webgl.drawElements(webgl.TRIANGLES, indices.length, webgl.UNSIGNED_BYTE, 0);
//绘制手掌
glMatrix.mat4.translate(mvpArr, mvpArr, [0, 10, 0]);
glMatrix.mat4.scale(mvpArr, mvpArr, [2, 0.2, 2]);
mvpArr = initTransformation(g_palm1Angle, [0, 1, 0], mvpArr);
webgl.drawElements(webgl.TRIANGLES, indices.length, webgl.UNSIGNED_BYTE, 0);
//绘制手指
let fingerPosition1 = glMatrix.mat4.create();
glMatrix.mat4.identity(fingerPosition1);
glMatrix.mat4.translate(fingerPosition1, mvpArr, [1, 10, 0]);
glMatrix.mat4.scale(fingerPosition1, fingerPosition1, [0.2, 5, 0.2]);
fingerPosition1 = initTransformation(g_finger1Angle, [0, 0, 1], fingerPosition1);
webgl.drawElements(webgl.TRIANGLES, indices.length, webgl.UNSIGNED_BYTE, 0);
let fingerPosition2 = glMatrix.mat4.create();
glMatrix.mat4.identity(fingerPosition2);
glMatrix.mat4.translate(fingerPosition2, mvpArr, [-1, 10, 0]);
glMatrix.mat4.scale(fingerPosition2, fingerPosition2, [0.2, 5, 0.2]);
mvpArr = initTransformation(g_finger1Angle, [0, 0, 1], fingerPosition2);
webgl.drawElements(webgl.TRIANGLES, indices.length, webgl.UNSIGNED_BYTE, 0);
}
//画圆柱
function drawHead() {
let positions = [];
let indicesArr = []
let SPHERE_DIV = 15;
let i, ai, si, ci;
let j, aj, sj, cj;
let p1, p2;
for (j = 0; j <= SPHERE_DIV; j++) {
aj = j * Math.PI / SPHERE_DIV;
sj = Math.sin(aj);
cj = Math.cos(aj);
for (i = 0; i <= SPHERE_DIV; i++) {
ai = i * 2 * Math.PI / SPHERE_DIV;//获取角度
si = Math.sin(ai);
ci = Math.cos(ai);
positions.push(ci ); // X
positions.push(cj); // Y
positions.push(si ); // Z
}
}
for (j = 0; j < SPHERE_DIV; j++) {
for (i = 0; i < SPHERE_DIV; i++) {
p1 = j * (SPHERE_DIV + 1) + i;
p2 = p1 + (SPHERE_DIV + 1);
indicesArr.push(p1);
indicesArr.push(p2);
indicesArr.push(p1 + 1);
indicesArr.push(p1 + 1);
indicesArr.push(p2);
indicesArr.push(p2 + 1);
}
}
let pointPosition = new Float32Array(positions);
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, 3, webgl.FLOAT, false, 0, 0);
let aNormal = webgl.getAttribLocation(webgl.program, "a_Normal");
let normalsBuffer = webgl.createBuffer();
let normalsArr = new Float32Array(positions);
webgl.bindBuffer(webgl.ARRAY_BUFFER, normalsBuffer);
webgl.bufferData(webgl.ARRAY_BUFFER, normalsArr, webgl.STATIC_DRAW);
webgl.enableVertexAttribArray(aNormal);
webgl.vertexAttribPointer(aNormal, 3, webgl.FLOAT, false, 0, 0);
let indexBuffer1 = webgl.createBuffer();
let indices1 = new Uint8Array(indicesArr);
webgl.bindBuffer(webgl.ELEMENT_ARRAY_BUFFER, indexBuffer1);
webgl.bufferData(webgl.ELEMENT_ARRAY_BUFFER, indices1, webgl.STATIC_DRAW);
let headPosition = glMatrix.mat4.create();
glMatrix.mat4.translate(headPosition, headPosition, [14.5, 22, 0]);
glMatrix.mat4.scale(headPosition, headPosition, [8, 8, 8]);
initTransformation(g_chest1Angle, [0, 1, 0], headPosition);
webgl.drawElements(webgl.TRIANGLES, indicesArr.length, webgl.UNSIGNED_BYTE, 0);
webgl.bindBuffer(webgl.ELEMENT_ARRAY_BUFFER, null);
webgl.deleteBuffer(indexBuffer1);
webgl.bindBuffer(webgl.ARRAY_BUFFER, null);
webgl.deleteBuffer(normalsBuffer);
webgl.bindBuffer(webgl.ARRAY_BUFFER, null);
webgl.deleteBuffer(triangleBuffer);
}
</script>
</head>
<body onload="init()">
<canvas id='myCanvas' width="1024" height='768' ></canvas>
<div id="text"></div>
</body>
</html>
3.核心详解:
3.1MVP(ModelViewProjection)变换:
3.1.1Model变换(模型在自身局部坐标系下进行平移+缩放+旋转(总称“仿射变换”)
在三维空间通常构造4*4的矩阵实现变换
- 平移:表现在4*4矩阵上就是在矩阵的最后一列进行平移指定距离
- 缩放:表现在4*4矩阵上就是矩阵的对角线上缩放对应比例
- 旋转:三维世界的旋转大致分为绕X轴、绕Y轴、绕Z轴进行旋转,旋转公式如下:
编辑
3.2View变换(相机引起的变换):相机由视向、up朝向、位置三要素所决定,为方便计算需对相机进行变换,变换后位置位于原点、视向为-z轴、up方向为y轴,x轴通过叉积得到。变换过程如下图:
编辑
编辑
3.3Projection变换(分正交投影[Orthographic]和透视投影[Perspective])
- 对于正交投影,只需通过平移和缩放变换,将其变换到[-1,1]*[-1,1]范围内即可
编辑
- 对于透视投影 ,先需要将其挤压成正交投影,然后再对其进行变换
挤压过程,需要利用齐次坐标、“两个规定”来求解相应变换矩阵
编辑
最后将两个矩阵相乘即可得到相应变换:
编辑
当然,对于左边那个矩阵,还可利用一些性质将其中参数简化,全部用aspect、fov等参数来代替
编辑
编辑
3.2gl-matrix.js中封装的变换矩阵
Model:
glMatrix.mat4.translate(ModelMatrix,ModelMatrix,[180,0,0]);
glMatrix.mat4.scale(armPosition, armPosition, [0.8, 1, 0.8]);
glMatrix.mat4.rotate(ModelMatrix, ModelMatrix, degreesToRads(angele), rotateArr);
View:
glMatrix.mat4.lookAt(ViewMatrix,[0,0,300],[0,0,-90],[0,1,0]);
Projection:
glMatrix.mat4.perspective(ProjMatrix,angle * Math.PI / 180, webglDiv.clientWidth/webglDiv.clientHeight,1,1000)
多层纹理
1.实现效果:
2.实现代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>多重纹理</title>
</head>
<body>
<!--canvas标签创建一个宽高均为500像素,背景为蓝色的矩形画布-->
<canvas id="webgl" width="500" height="500" style="background-color: greenyellow"></canvas>
<!-- 顶点着色器源码 -->
<script id="vertexShader" type="x-shader/x-vertex">
attribute vec4 a_Position;//顶点位置坐标
attribute vec2 a_TexCoord;//纹理坐标
varying vec2 v_TexCoord;//插值后纹理坐标
void main() {
//顶点坐标apos赋值给内置变量gl_Position
gl_Position = a_Position;
//纹理坐标插值计算
v_TexCoord = a_TexCoord;
}
</script>
<!-- 片元着色器源码 -->
<script id="fragmentShader" type="x-shader/x-fragment">
//所有float类型数据的精度是highp
precision highp float;
// 接收插值后的纹理坐标
varying vec2 v_TexCoord;
// 纹理图片像素数据
uniform sampler2D u_Sampler;
uniform sampler2D u_Sampler1;
uniform float u_Ani;
void main() {
// 采集纹素,逐片元赋值像素值
vec4 color1 = texture2D(u_Sampler,v_TexCoord);
vec4 color2 = texture2D(u_Sampler1,vec2(v_TexCoord.x + u_Ani , v_TexCoord.y));
gl_FragColor = color1 + color2;
}
</script>
<script>
//通过getElementById()方法获取canvas画布
var canvas = document.getElementById('webgl');
//通过方法getContext()获取Gl上下文
var gl = canvas.getContext('webgl');
//顶点着色器源码
var vertexShaderSource = document.getElementById('vertexShader').innerText;
//片元着色器源码
var fragShaderSource = document.getElementById('fragmentShader').innerText;
var count = 0;
//初始化着色器
var program = initShader(gl, vertexShaderSource, fragShaderSource);
var a_Position = gl.getAttribLocation(program, 'a_Position');
var a_TexCoord = gl.getAttribLocation(program, 'a_TexCoord');
var u_Sampler = gl.getUniformLocation(program, 'u_Sampler');
var u_Sampler1 = gl.getUniformLocation(program, 'u_Sampler1');
var u_Ani = gl.getUniformLocation(program, 'u_Ani');
//拉伸改变其坐标值
var data = new Float32Array([
-0.5, 0.5, //左上角——v0
-0.5, -0.5, //左下角——v1
0.5, 0.5, //右上角——v2
0.5, -0.5 //右下角——v3
]);
/**
* 创建UV纹理坐标数据textureData
**/
var textureData = new Float32Array([
0, 1, //左上角——uv0
0, 0, //左下角——uv1
1, 1, //右上角——uv2
1, 0 //右下角——uv3
]);
//创建缓冲区对象
var buffer = gl.createBuffer();
//绑定缓冲区对象,激活buffer
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
//顶点数组data数据传入缓冲区
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
//缓冲区中的数据按照一定的规律传递给位置变量apos
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
//允许数据传递
gl.enableVertexAttribArray(a_Position);
//创建缓冲区对象
var textureBuffer = gl.createBuffer();
//绑定缓冲区对象,激活buffer
gl.bindBuffer(gl.ARRAY_BUFFER, textureBuffer);
//顶点数组data数据传入缓冲区
gl.bufferData(gl.ARRAY_BUFFER, textureData, gl.STATIC_DRAW);
//缓冲区中的数据按照一定的规律传递给位置变量apos
gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, 0, 0);
//允许数据传递
gl.enableVertexAttribArray(a_TexCoord);
//要调用呀!写了函数不用是什么鬼
loop();
/**
创建缓冲区textureBuffer,传入图片纹理数据,然后执行绘制方法drawArrays()
**/
texture1 = initTexture("fog.png");
texture0 = initTexture("山水.png");
function handleLoadedTexture(texture) {
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
// gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
// gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
}
function initTexture(imageFile) {
let textureHandle = gl.createTexture();
textureHandle.image = new Image();
textureHandle.image.src = imageFile;
textureHandle.image.onload = function () {
handleLoadedTexture(textureHandle)
}
return textureHandle;
}
function loop() {
requestAnimationFrame(loop);
draw();
}
function draw() {
gl.clearColor(0.0, 1.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.enable(gl.DEPTH_TEST);
count = count + 0.01;
// console.log(count);
gl.uniform1f(u_Ani, count);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture0);
gl.uniform1i(u_Sampler, 0);
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, texture1);
gl.uniform1i(u_Sampler1, 1);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
//声明初始化着色器函数
function initShader(gl, vertexShaderSource, fragmentShaderSource) {
//创建着色器对象
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
//引入着色器源代码
gl.shaderSource(vertexShader, vertexShaderSource);
gl.shaderSource(fragmentShader, fragmentShaderSource);
//编译着色器
gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);
//创建程序对象program
var program = gl.createProgram();
//附着着色器到program
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
//链接program
gl.linkProgram(program);
//使用program
gl.useProgram(program);
//返回程序program对象
return program;
};
</script>
</body>
</html>
3.核心拆解:
3.1 片元着色器中调用 texture2D(texture,texcoord),方法有两个参数,一个为所需要的纹理,另一部分为纹理坐标。纹理坐标好办,创建类型化数组传递即可。本案例涉及多重纹理叠加,所以最后片源着色器的颜色为两种颜色的和。
vec4 color1 = texture2D(u_Sampler,v_TexCoord);
vec4 color2 = texture2D(u_Sampler1,vec2(v_TexCoord.x + u_Ani , v_TexCoord.y));
gl_FragColor = color1 + color2;
本案例还涉及动态雾,所以需要利用requestAnimationFrame()方法,进行不断绘制。
function tick() {
requestAnimFrame(tick)
draw();
};
下面重点讲述纹理数据的创建与传递,纹理的创建可参考缓冲区的创建:
1.webgl.createTexture创建纹理对象
2.配置纹理对象的属性 .image .image.src .image.onload
3.绑定纹理、设置二维纹理、设置纹理属性,将其封装归类到handleLoaderTexture()方法中
4.最后在绘制函数draw()中激活、绑定、传递纹理数据
function handleLoadedTexture(texture) {
webgl.bindTexture(webgl.TEXTURE_2D, texture);
webgl.texImage2D(webgl.TEXTURE_2D, 0, webgl.RGBA, webgl.RGBA, webgl.UNSIGNED_BYTE, texture.image);
webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_MAG_FILTER, webgl.LINEAR);
webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_MIN_FILTER, webgl.LINEAR);
webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_S, webgl.REPEAT);
webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_T, webgl.REPEAT);
// webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_S, webgl.CLAMP_TO_EDGE);
// webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_T, webgl.CLAMP_TO_EDGE);
}
function initTexture(imageFile, num) {
let textureHandle = webgl.createTexture();
textureHandle.image = new Image();
textureHandle.image.src = imageFile;
textureHandle.image.onload = function () {
handleLoadedTexture(textureHandle, num)
}
return textureHandle;
}
function draw() {
webgl.clearColor(0.0, 1.0, 0.0, 1.0);
webgl.clear(webgl.COLOR_BUFFER_BIT | webgl.DEPTH_BUFFER_BIT);
webgl.enable(webgl.DEPTH_TEST);
//纹理变动
uniformAnim = webgl.getUniformLocation(webgl.program, "anim");
count = count + 0.01;
webgl.uniform1f(uniformAnim, count);
webgl.activeTexture(webgl.TEXTURE0);
webgl.bindTexture(webgl.TEXTURE_2D, texture0);
webgl.uniform1i(uniformTexture, 0);
webgl.activeTexture(webgl.TEXTURE1);
webgl.bindTexture(webgl.TEXTURE_2D, texture1);
webgl.uniform1i(uniformTexture1, 1);
webgl.drawArrays(webgl.TRIANGLES, 0, 6);
}
(注:在WebGL渲染纹理时,纹理的像素也有要求的,需要为2的幂次方,所以有时用自己的纹理图片去贴图时,明明代码都一样却总出来一个黑色照片,不是代码的问题,是原始数据的问题)