3D基本元素
2D坐标系
2D坐标系由x轴和y轴构成。其中,笛卡尔坐标系是最常见的2D坐标系。
笛卡尔坐标系
规定y轴向上为正方向,x轴向右为正方向
HTML5 Canvas 2D坐标系
原点(0,0)为左上角,向右为x轴的正方向,向下为y轴的正方向
3D坐标系
在2D坐标系上增加了表示深度的z轴,即3D物体里屏幕的深度
HTML5 WebGL 3D坐标系
绘图的可是范围为[-1,+1]
网格与3D图形
- 三角形是基础图形
- 3D图形由一个或多个三角形组成
- 网格由一个或多个图形组成,网格也叫模型
法线
始终垂直于某平面的虚线。在几何中,法线是指平面上垂直于曲线在某点切线的直线。
3D变换概念
- 变换原理是对顶点的改变
- 变换的缩放、平移和旋转
矩阵
- 逐个顶点的变换方法是复杂乏味的
- 矩阵是复杂性解决之道
网格表面
纹理与材质
一个网格构成了物体的形状,纹理可以定义网格表面的外观
- 纹理可以定义网格表面的外观
- 材质是网格表面的特性
光照原理
- 光线方向决定物体的明暗度与阴影
- 在着色过程中,需要考虑光源类型与反射类型
光源类型
- 平行光
- 点光源光
- 环境光
反射类型
- 漫反射:光色由入射光色、表面漆色和入射角决定
- 环境反射:由入射光色和表面漆色决定
着色器
替代传统的固定渲染管线
可编程性
顶点着色器
顶点着色器用来描述顶点的位置、颜色的程序
片元着色器
片元着色器是对网格表面像素的处理程序。
3D 世界与立方体
- 立方体的构成
一个正方形的立方体在WebGL程序中,实际上是绘制了12个三角形,再对它的表面进行着色,最终形成我们看到的效果。
- 模型文件格式
相机、视口和投影
左下角的眼睛代表相机,相机角度的不同,观察到的场景的景象就是不同的。而它的远近(x)就决定了他到的物体的大小。图中的绿色区域代表视口,通俗的讲就是WebGL Canvas决定的,它的大小也就决定了它可视化的范围。后面红色的区域能够显示场景的最远距离到哪里。红绿块之间的可视化椎体,就决定了可视化世界的范围。最终的影像会在可视化视口中呈现出来。
投影的概念就是物体投影到前方视口上最终形成的影像
通过对相机角度的不同,它的椎体肯定是有变化的,它看到的投影到视口上的影像肯定也是不同的。
第一个WebGL程序
开发WebGL程序的基本步骤
- 得到canvas标签
- 得到绘制上下文对象
- 编写着色器
- 初始化着色器
- 绘制
- 代码实现
<!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>第一个WebGL程序</title>
<script src="./lib.js"></script>
</head>
<body>
<canvas id="webgl" width="500" height="500"></canvas>
<script>
var canvasElement = document.getElementById('webgl')
const context = canvasElement.getContext('webgl')
// 绘制顶点着色器
const vertexShaderSource = `
attribute vec4 apos;
void main() {
gl_PointSize=20.0;
gl_Position = apos;
}
`
// 绘制片元着色器
const fragShaderSource = `
void main(){
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
`
const program = initShader(context, vertexShaderSource, fragShaderSource)
const aposLocation = context.getAttribLocation(program, 'apos')
context.clearColor(0.2, 0.3, 0.5, 1.0)
context.clear(context.COLOR_BUFFER_BIT)
let x = 0.0
let y = 0.0
for(var i = 0, j = 0.1; i< 10; i++){
x += y
y += j
context.vertexAttrib4f(aposLocation, x, y, 0.0, 1.0)
context.drawArrays(context.POINTS, 0, 1)
}
</script>
</body>
</html>
// lib.js
/**
*
* @param {CanvasContext} gl Canvas Context 对象
* @param {*} vertexShaderSource 顶点着色器源码
* @param {*} fragmentShaderSource 片元着色器源码
* @returns
*/
const initShader = function(gl, vertexShaderSource, fragmentShaderSource) {
// 创建一个空的顶点着色器对象
const vertexShader = gl.createShader(gl.VERTEX_SHADER)
// 创建一个空的片元着色器对象
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
// 加入源代码
gl.shaderSource(vertexShader, vertexShaderSource)
gl.shaderSource(fragmentShader, fragmentShaderSource)
// 编译源代码
gl.compileShader(vertexShader)
gl.compileShader(fragmentShader)
// 创建内部调用的应用程序
var program = gl.createProgram()
// 将着色器附着在应用程序上
gl.attachShader(program, vertexShader)
gl.attachShader(program, fragmentShader)
// 连接
gl.linkProgram(program)
gl.useProgram(program)
// 输出调试日志
console.log(gl.getShaderInfoLog(fragmentShader))
return program
}
WebGL 缓冲区对象
attribute 变量的使用
- 在顶点着色器中,声明 attribute 变量
- 将 attribute 变量赋值给 gl_Position 变量
- 向 attribute 变量传递数据
缓冲区对象的创建与绑定
缓冲区对象是绘制面所必须的技术,否则只能绘制点,它可以一次性地写入多个顶点数据,这样就可以为我们描绘出一个图形的轮廓,它可以是一块内存区域,这块内存区域通常来说很可能是显卡分配的
创建步骤
1、创建缓冲区的方法 var buffer = gl.createBuffer()
2、根据返回值判断是否创建成功
3、绑定缓冲区方法 gl.bindBuffer(gl.ARRAY_BUFFER,buffer)
向缓冲区内写入数据
gl.bufferData(gl.ARRAY_BUFFER, data, useMethod)
- data:是类型化数组
useMethod:如何使用内存中的数据,可选值有:
- gl.STATIC_DRAW 一次性写入,多次绘制
- gl.STREAM_DRAW 一次性写入,调用几次,比第一次调用的要少,但都是写入一次
- gl.DYNAMIC_DRAW 多次写入,多次绘制
类型化数组:
- Int8Array Unit8Array
- Int16Array Unit16Array
- Int32Array Unit32Array
- Float32Array 最常用
- Float64Array
缓冲区数据导入 attribute变量
vertexAttribPointer(location, size, type, normalized, stride, offset)
完整代码示例
<!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="./lib.js"></script>
</head>
<body>
<canvas id="webglcanvas" width="500" height="500"></canvas>
<script>
var ctx = document.getElementById('webglcanvas').getContext('webgl')
var vertexShaderSource = `
attribute vec4 apos;
void main(){
gl_Position = apos;
gl_PointSize = 15.0;
}
`
var fragmentShaderSource = `
void main(){
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
`
var buf = ctx.createBuffer()
ctx.bindBuffer(ctx.ARRAY_BUFFER, buf)
var program = initShader(ctx,vertexShaderSource,fragmentShaderSource)
var aposLocation = ctx.getAttribLocation(program, 'apos')
var data = new Float32Array([
0.6,0.8,
0.2,0.5,
-1.0,1.0,
1.0,1.0,
-1.0,-1.0,
1.0,-1.0,
0.0,0.0
])
ctx.bufferData(ctx.ARRAY_BUFFER, data, ctx.STATIC_DRAW)
ctx.vertexAttribPointer(aposLocation, 2, ctx.FLOAT, false, 0, 0)
ctx.enableVertexAttribArray(aposLocation)
ctx.clearColor(0.0,0.0,1.0,1.0)
ctx.clear(ctx.COLOR_BUFFER_BIT)
// var points = [
// {x:0, y:0},
// {x:0.6, y:0.8},
// {x:0.2, y:0.5}
// ]
// for(var i = 0; i< points.length; i++) {
// ctx.vertexAttrib4f(aposLocation, points[i].x, points[i].y, 1.0, 1.0)
// ctx.drawArrays(ctx.POINTS, 0, 3)
// }
ctx.drawArrays(ctx.POINTS, 0, 7)
</script>
</body>
</html>
WebGL 基本图形绘制
点的绘制
gl.drawArrays(gl.POINTS, 0, 1)
线段的绘制
绘制线
gl.drawArrays(gl.LINES, start, count)
var vertexShaderSource = `
attribute vec4 aPos;
void main() {
gl_Position = aPos;
gl_PointSize = 20.5;
}
`
var fragmentShaderSource = `
void main() {
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
`
var gl = document.getElementById('canvas').getContext('webgl')
var program = initShader(gl,vertexShaderSource,fragmentShaderSource)
var posLocation = gl.getAttribLocation(program, 'aPos')
var data = new Float32Array([
0.0,0.0,0.5,0.5,
0.3,0.6,-0.3,-0.9
])
var buffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW)
gl.vertexAttribPointer(posLocation, 2, gl.FLOAT, false, 0, 0)
gl.enableVertexAttribArray(posLocation)
gl.clearColor(0.0, 0.0, 0.0, 1.0)
gl.clear(gl.COLOR_BUFFER_BIT)
gl.drawArrays(gl.LINES, 0, 4)
运行效果:
绘制多线段
gl.drawArray(gl.LINE_STRIP, start, count)
绘制Z形线
var vertexShaderSource = `
attribute vec4 aPos;
void main() {
gl_Position = aPos;
gl_PointSize = 20.5;
}
`
var fragmentShaderSource = `
void main() {
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
`
var gl = document.getElementById('canvas').getContext('webgl')
var program = initShader(gl,vertexShaderSource,fragmentShaderSource)
var posLocation = gl.getAttribLocation(program, 'aPos')
var data = new Float32Array([
-0.5,0.5,
0.5,0.5,
-0.5,-0.5,
0.5,-0.5
])
var buffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW)
gl.vertexAttribPointer(posLocation, 2, gl.FLOAT, false, 0, 0)
gl.enableVertexAttribArray(posLocation)
gl.clearColor(0.0, 0.0, 0.0, 1.0)
gl.clear(gl.COLOR_BUFFER_BIT)
gl.drawArrays(gl.LINE_STRIP, 0, 4)
绘制矩形框
// 绘制矩形框
var vertexShaderSource = `
attribute vec4 aPos;
void main() {
gl_Position = aPos;
gl_PointSize = 20.5;
}
`
var fragmentShaderSource = `
void main() {
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
`
var gl = document.getElementById('canvas').getContext('webgl')
var program = initShader(gl,vertexShaderSource,fragmentShaderSource)
var posLocation = gl.getAttribLocation(program, 'aPos')
var data = new Float32Array([
-0.5,0.5,
0.5,0.5,
0.5,-0.5,
-0.5,-0.5,
-0.5,0.5
])
var buffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW)
gl.vertexAttribPointer(posLocation, 2, gl.FLOAT, false, 0, 0)
gl.enableVertexAttribArray(posLocation)
gl.clearColor(0.0, 0.0, 0.0, 1.0)
gl.clear(gl.COLOR_BUFFER_BIT)
gl.drawArrays(gl.LINE_STRIP, 0, 5)
绘制回路线段
gl.drawArray(gl.LING_LOOP,start, count)
可以把最后一个顶点跟第一个顶点连接在一起
// 绘制回路线段
var vertexShaderSource = `
attribute vec4 aPos;
void main() {
gl_Position = aPos;
gl_PointSize = 20.5;
}
`
var fragmentShaderSource = `
void main() {
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
`
var gl = document.getElementById('canvas').getContext('webgl')
var program = initShader(gl,vertexShaderSource,fragmentShaderSource)
var posLocation = gl.getAttribLocation(program, 'aPos')
var data = new Float32Array([
-0.5,0.5,
0.5,0.5,
0.5,-0.5,
-0.5,-0.5
])
var buffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW)
gl.vertexAttribPointer(posLocation, 2, gl.FLOAT, false, 0, 0)
gl.enableVertexAttribArray(posLocation)
gl.clearColor(0.0, 0.0, 0.0, 1.0)
gl.clear(gl.COLOR_BUFFER_BIT)
gl.drawArrays(gl.LINE_LOOP, 0, 4)
多边形的绘制
gl.drawArray(gl.TRIANGLES, start, count)
// 绘制三角形
var vertexShaderSource = `
attribute vec4 aPos;
void main() {
gl_Position = aPos;
gl_PointSize = 20.5;
}
`
var fragmentShaderSource = `
void main() {
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
`
var gl = document.getElementById('canvas').getContext('webgl')
var program = initShader(gl,vertexShaderSource,fragmentShaderSource)
var posLocation = gl.getAttribLocation(program, 'aPos')
var data = new Float32Array([
0.0,0.0,
-0.5,-0.5,
0.5,-0.5
])
var buffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW)
gl.vertexAttribPointer(posLocation, 2, gl.FLOAT, false, 0, 0)
gl.enableVertexAttribArray(posLocation)
gl.clearColor(0.0, 0.0, 0.0, 1.0)
gl.clear(gl.COLOR_BUFFER_BIT)
gl.drawArrays(gl.TRIANGLES, 0, 3)
绘制三角带
gl.drawArray(gl.TRIANGLE_STRIP,start, count)
// 绘制平行四边形
var vertexShaderSource = `
attribute vec4 aPos;
void main() {
gl_Position = aPos;
gl_PointSize = 20.5;
}
`
var fragmentShaderSource = `
void main() {
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
`
var gl = document.getElementById('canvas').getContext('webgl')
var program = initShader(gl,vertexShaderSource,fragmentShaderSource)
var posLocation = gl.getAttribLocation(program, 'aPos')
var data = new Float32Array([
-0.3,0.3,
0.5,0.3,
-0.5,-0.3,
0.3, -0.3
])
var buffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW)
gl.vertexAttribPointer(posLocation, 2, gl.FLOAT, false, 0, 0)
gl.enableVertexAttribArray(posLocation)
gl.clearColor(0.0, 0.0, 0.0, 1.0)
gl.clear(gl.COLOR_BUFFER_BIT)
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4)
绘制三角扇
gl.drawArray(gl.TRIANGLE_FAN, start, count)
// 绘制六边形
var vertexShaderSource = `
attribute vec4 aPos;
void main() {
gl_Position = aPos;
gl_PointSize = 20.5;
}
`
var fragmentShaderSource = `
void main() {
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
`
var gl = document.getElementById('canvas').getContext('webgl')
var program = initShader(gl,vertexShaderSource,fragmentShaderSource)
var posLocation = gl.getAttribLocation(program, 'aPos')
var data = new Float32Array([
0.0,0.0,
-0.3,0.5,
-0.6,0.0,
-0.3,-0.5,
0.3,-0.5,
0.6,0.0,
0.3,0.5,
-0.3,0.5
])
var buffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW)
gl.vertexAttribPointer(posLocation, 2, gl.FLOAT, false, 0, 0)
gl.enableVertexAttribArray(posLocation)
gl.clearColor(0.0, 0.0, 0.0, 1.0)
gl.clear(gl.COLOR_BUFFER_BIT)
gl.drawArrays(gl.TRIANGLE_FAN, 0, 8)
WebGL 图形变换
图形的移动
原理
图形的移动,实际上就是定量改变顶点的位置,在顶点着色器中,顶点修改是逐个进行的,且偏移值一致
平移代码示例
var main = function() {
// 顶点着色器
var vertexShaderSource = `
attribute vec4 pos;
uniform float a;
uniform float b;
void main() {
gl_Position.x = pos.x + a;
gl_Position.y = pos.y + b;
gl_Position.z = 0.0;
gl_Position.w = 1.0;
}
`
// 片元着色器
var fragmentShaderSource = `
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`
var gl = document.getElementById('canvas').getContext('webgl')
var buffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
var data = new Float32Array([
0.0,0.0,
-0.5,-0.5,
0.5,-0.5
])
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW)
var program = initShader(gl, vertexShaderSource, fragmentShaderSource)
var a = 0.0, b = 0.0
var posLocation = gl.getAttribLocation(program, 'pos')
var aLocation = gl.getUniformLocation(program, 'a')
var bLocation = gl.getUniformLocation(program, 'b')
gl.vertexAttribPointer(posLocation, 2, gl.FLOAT, false, 0, 0)
gl.enableVertexAttribArray(posLocation)
var run = function() {
gl.uniform1f(aLocation, a)
gl.uniform1f(bLocation, b)
gl.clearColor(0.0, 0.0, 1.0, 1.0)
gl.clear(gl.COLOR_BUFFER_BIT)
gl.drawArrays(gl.TRIANGLES, 0, 3)
a += 0.05
b += 0.05
setTimeout(run, 50)
}
run()
}
main()
图形的缩放
顶点不变的方式缩放
var main = function() {
// 顶点着色器
var vertexShaderSource = `
attribute vec4 pos;
uniform float zoomRadio;
void main() {
gl_Position.x = pos.x * zoomRadio;
gl_Position.y = pos.y * zoomRadio;
gl_Position.z = 0.0;
gl_Position.w = 1.0;
}
`
// 片元着色器
var fragmentShaderSource = `
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`
var gl = document.getElementById('canvas').getContext('webgl')
var buffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
var data = new Float32Array([
0.0,0.0,
-0.5,-0.5,
0.5,-0.5
])
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW)
var program = initShader(gl, vertexShaderSource, fragmentShaderSource)
var zoomRatio = Math.random()
var posLocation = gl.getAttribLocation(program, 'pos')
var zoomLocation = gl.getUniformLocation(program, 'zoomRadio')
gl.vertexAttribPointer(posLocation, 2, gl.FLOAT, false, 0, 0)
gl.enableVertexAttribArray(posLocation)
var run = function() {
gl.uniform1f(zoomLocation, zoomRatio)
gl.clearColor(0.0, 0.0, 1.0, 1.0)
gl.clear(gl.COLOR_BUFFER_BIT)
gl.drawArrays(gl.TRIANGLES, 0, 3)
zoomRatio = Math.random()
setTimeout(run, 500)
}
run()
}
main()
图形的旋转
var main = function() {
// 顶点着色器
var vertexShaderSource = `
attribute vec4 pos;
uniform float sinB;
uniform float cosB;
void main() {
gl_Position.x = pos.x * cosB - pos.y * sinB;
gl_Position.y = pos.x * sinB + pos.y * cosB;
gl_Position.z = 0.0;
gl_Position.w = 1.0;
}
`
// 片元着色器
var fragmentShaderSource = `
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`
var gl = document.getElementById('canvas').getContext('webgl')
var data = new Float32Array([
0.0,0.0,
-0.5,-0.5
])
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW)
var program = initShader(gl, vertexShaderSource, fragmentShaderSource)
bindAttribute(gl, 'pos', data, program)
var sinB, sinBLocation,cosB, cosBLocation
sinBLocation = gl.getUniformLocation(program, 'sinB')
cosBLocation = gl.getUniformLocation(program, 'cosB')
var angle = -135;
setInterval(function() {
angle -= 1
var t = Math.PI * angle / 180
sinB = Math.sin(t)
cosB = Math.cos(t)
gl.uniform1f(sinBLocation, sinB)
gl.uniform1f(cosBLocation,cosB)
gl.clearColor(0.0, 0.0, 1.0, 1.0)
gl.clear(gl.COLOR_BUFFER_BIT)
gl.drawArrays(gl.LINES, 0, 2)
}, 1000)
}
main()
评论 (0)