1. 概述
之前的学习都是在单个着色器上进行的,如果你要绘制不同的图形,必然需要不同的着色器来处理。好在webgl
能够随时切换着色器,还记得useProgram
这个方法吗,这个就是切换着色器的关键。
2. 实践
现在我们有两个着色器画两个立方体,一个透明的一个不透明的。
本质上画两个和画一个是一样的,只是在画另一个的时候需要切换程序program
。所以我会将通用的部分简单封装一下(随便封装的没有参考价值哈)。
// 封装部分操作
import { ReadonlyVec3, glMatrix, mat4 } from "gl-matrix"
import { getModelMat } from "./utils"
class WebGLLib {
glContext: WebGLRenderingContext
private programMap: Map<string, WebGLProgram> = new Map()
// 当前使用的program
program: WebGLProgram
private uniformMap: Map<string, WebGLUniformLocation> = new Map()
private indexBufferLength: number
constructor(public canvas: HTMLCanvasElement) {
this.glContext = canvas.getContext('webgl')
}
/**
* 创建program
*/
createProgram(key: string, vertexCode: string, fragmentCode: string) {
const gl = this.glContext
// 1.创建顶点、片元着色器
const vertexShader = gl.createShader(gl.VERTEX_SHADER)
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
// 2.装载着色器代码
gl.shaderSource(vertexShader, vertexCode)
gl.shaderSource(fragmentShader, fragmentCode)
// 3.编译着色器
gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);
// 4.创建WebGLProgram对象
const program = gl.createProgram();
// 5.添加着色器
gl.attachShader(program, vertexShader)
gl.attachShader(program, fragmentShader)
// 6.链接程序
gl.linkProgram(program);
this.programMap.set(key, program)
}
/**
* 使用program
*/
use(key: string) {
const program = this.programMap.get(key)
if(program) {
// 7.WebGLProgram对象添加到渲染状态中
this.glContext.useProgram(program)
this.program = program
}
}
/**
* 绑定顶点缓冲
*/
bindVertexBuffer(
bufferData: BufferSource, attribName: string,
size: number, type: number = WebGLRenderingContext.FLOAT, stride: number = 0, offset: number = 0
) {
const gl = this.glContext
const program = this.program
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
gl.bufferData(gl.ARRAY_BUFFER, bufferData, gl.STATIC_DRAW)
const attrib = gl.getAttribLocation(program, attribName)
gl.vertexAttribPointer(attrib, size, type, false, stride, offset)
gl.enableVertexAttribArray(attrib)
}
/**
* 绑定索引
*/
bindIndexBuffer(bufferData: Uint8Array) {
const gl = this.glContext
var indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, bufferData, gl.STATIC_DRAW);
this.indexBufferLength = bufferData.length
}
/**
* 设置uniform值
*/
setUniformValue(uniformName: string, value: GLfloat[]) {
const cacheUniform = this.uniformMap.get(uniformName)
const gl = this.glContext
const program = this.program
const uniform = cacheUniform ?? gl.getUniformLocation(program, uniformName)
this.uniformMap.set(uniformName, uniform)
const key = `uniform${value.length}fv` as 'uniform1fv'
gl[key](uniform, value)
}
setUniformMatrixValue(uniformName: string, value: mat4) {
const cacheUniform = this.uniformMap.get(uniformName)
const gl = this.glContext
const program = this.program
const uniform = cacheUniform ?? gl.getUniformLocation(program, uniformName)
this.uniformMap.set(uniformName, uniform)
gl.uniformMatrix4fv(uniform, false, value)
}
/**
* 开启功能
*/
enables(caps: number[]) {
caps.forEach(c => this.glContext.enable(c))
}
/**
* 获取mvp矩阵
*/
getMVP({
perspective = {},
lookAt = {},
model = {}
}: GetMVPParams) {
const {
fov = 10,
aspect = this.canvas.width / this.canvas.height,
near = 1,
far = 1000
} = perspective
const {
eye = [10, 10, 30],
center = [0, 0, 0],
up = [0, 1, 0],
} = lookAt
const {
translate,
rotate,
scale
} = model
const perspectiveMat = mat4.perspective(mat4.create(), glMatrix.toRadian(fov), aspect, near, far)
const viewMat = mat4.lookAt(mat4.create(), eye, center, up)
const vpMat = mat4.multiply(mat4.create(), perspectiveMat, viewMat)
const modelMat = getModelMat(translate, rotate, scale)
const mvpMat = mat4.multiply(mat4.create(), vpMat, modelMat)
return {
perspectiveMat,
viewMat,
modelMat,
mvpMat
}
}
/**
* 绘制
*/
draw(clear: boolean = true) {
const gl = this.glContext
if(clear) {
gl.clearColor(0, 0, 0, 1)
gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT)
}
gl.drawElements(gl.TRIANGLES, this.indexBufferLength, gl.UNSIGNED_BYTE, 0);
}
}
export default WebGLLib
interface GetMVPParams {
perspective?: {
fov?: number,
aspect?: number,
near?: number,
far?: number
},
lookAt?: {
eye?: ReadonlyVec3
center?: ReadonlyVec3
up?: ReadonlyVec3
},
model?: {
translate?: ReadonlyVec3,
rotate?: ReadonlyVec3,
scale?: ReadonlyVec3
}
}
就是把常用的模板代码放进通用方法里面了。
// 使用
const Lib = new WebGLLib(document.getElementById('webgl') as HTMLCanvasElement)
Lib.createProgram('normalCube', vertexCode, fragmentCode)
Lib.createProgram('transparentCube', vertexCode, fragmentCode)
// 绘制普通立方体
Lib.use('normalCube');
// 绑定顶点缓冲
Lib.bindVertexBuffer()
// 绑定颜色缓冲
Lib.bindVertexBuffer()
// .....
// 绘制透明立方体
Lib.use('normalCube');
// .....
上面简化代码已经揭示了切换着色器的方法,就是切换program
进行操作。
3. 完整代码
存一下档
// 顶点着色器
// mvp矩阵
uniform mat4 mvpMat;
// model矩阵
uniform mat4 modelMat;
// 顶点位置
attribute vec4 pos;
// 顶点颜色
attribute vec4 color;
// 初始位置法线向量
attribute vec4 originalNormal;
varying vec4 _color;
varying vec4 _originalNormal;
varying vec4 _vertexPosition;
void main(){
// 绘制立方体顶点
gl_Position = mvpMat * pos;
gl_PointSize = 10.0;
// 计算顶点世界坐标
_vertexPosition = modelMat * pos;
// 法向量是存储在顶点缓冲区的,所以只能间接传递给片元着色器
_originalNormal = originalNormal;
_color = color;
}
// 片元着色器
precision mediump float;
// 光源颜色
uniform vec3 lightColor;
// 光源方向
uniform vec3 lightDirection;
// 环境光颜色
uniform vec3 ambientColor;
// 法向量变换矩阵 也就是逆转置矩阵
uniform mat4 normalMat;
varying vec4 _color;
varying vec4 _originalNormal;
varying vec4 _vertexPosition;
void main(){
// 计算该顶点的光线方向
vec3 vertexLightDirection = normalize(lightDirection - vec3(_vertexPosition));
// 计算变换后的法向量
vec3 normal = normalize(vec3(normalMat * _originalNormal));
// 计算反射颜色
float cos = max(dot(vertexLightDirection, normal), 0.0);
vec3 diffuse = lightColor * _color.rgb * cos;
// 计算环境反射光
vec3 ambient = ambientColor * _color.rgb;
vec4 objectFaceColor = vec4(diffuse + ambient, _color.a);
gl_FragColor = objectFaceColor;
}
import { ReadonlyVec3, glMatrix, mat4 } from "gl-matrix"
import { getModelMat } from "./utils"
class WebGLLib {
currentKey: string
glContext: WebGLRenderingContext
private programMap: Map<string, WebGLProgram> = new Map()
// 当前使用的program
program: WebGLProgram
private uniformMap: Map<string, WebGLUniformLocation> = new Map()
private indexBufferLength: number
constructor(public canvas: HTMLCanvasElement) {
this.glContext = canvas.getContext('webgl')
}
/**
* 创建program
*/
createProgram(key: string, vertexCode: string, fragmentCode: string) {
const gl = this.glContext
// 1.创建顶点、片元着色器
const vertexShader = gl.createShader(gl.VERTEX_SHADER)
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
// 2.装载着色器代码
gl.shaderSource(vertexShader, vertexCode)
gl.shaderSource(fragmentShader, fragmentCode)
// 3.编译着色器
gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);
// 4.创建WebGLProgram对象
const program = gl.createProgram();
// 5.添加着色器
gl.attachShader(program, vertexShader)
gl.attachShader(program, fragmentShader)
// 6.链接程序
gl.linkProgram(program);
this.programMap.set(key, program)
}
/**
* 使用program
*/
use(key: string) {
const program = this.programMap.get(key)
if(program) {
// 7.WebGLProgram对象添加到渲染状态中
this.glContext.useProgram(program)
this.program = program
this.currentKey = key
}
}
/**
* 绑定顶点缓冲
*/
bindVertexBuffer(
bufferData: BufferSource, attribName: string,
size: number, type: number = WebGLRenderingContext.FLOAT, stride: number = 0, offset: number = 0
) {
const gl = this.glContext
const program = this.program
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
gl.bufferData(gl.ARRAY_BUFFER, bufferData, gl.STATIC_DRAW)
const attrib = gl.getAttribLocation(program, attribName)
gl.vertexAttribPointer(attrib, size, type, false, stride, offset)
gl.enableVertexAttribArray(attrib)
}
/**
* 绑定索引
*/
bindIndexBuffer(bufferData: Uint8Array) {
const gl = this.glContext
var indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, bufferData, gl.STATIC_DRAW);
this.indexBufferLength = bufferData.length
}
/**
* 设置uniform值
*/
setUniformValue(uniformName: string, value: GLfloat[]) {
const cacheUniform = this.uniformMap.get(`${this.currentKey}-${uniformName}`)
const gl = this.glContext
const program = this.program
const uniform = cacheUniform ?? gl.getUniformLocation(program, uniformName)
this.uniformMap.set(`${this.currentKey}-${uniformName}`, uniform)
const key = `uniform${value.length}fv` as 'uniform1fv'
gl[key](uniform, value)
}
setUniformMatrixValue(uniformName: string, value: mat4) {
const cacheUniform = this.uniformMap.get(`${this.currentKey}-${uniformName}`)
const gl = this.glContext
const program = this.program
const uniform = cacheUniform ?? gl.getUniformLocation(program, uniformName)
this.uniformMap.set(`${this.currentKey}-${uniformName}`, uniform)
gl.uniformMatrix4fv(uniform, false, value)
}
/**
* 开启功能
*/
enables(caps: number[]) {
caps.forEach(c => this.glContext.enable(c))
}
/**
* 获取mvp矩阵
*/
getMVP({
perspective = {},
lookAt = {},
model = {}
}: GetMVPParams) {
const {
fov = 10,
aspect = this.canvas.width / this.canvas.height,
near = 1,
far = 1000
} = perspective
const {
eye = [10, 10, 30],
center = [0, 0, 0],
up = [0, 1, 0],
} = lookAt
const {
translate,
rotate,
scale
} = model
const perspectiveMat = mat4.perspective(mat4.create(), glMatrix.toRadian(fov), aspect, near, far)
const viewMat = mat4.lookAt(mat4.create(), eye, center, up)
const vpMat = mat4.multiply(mat4.create(), perspectiveMat, viewMat)
const modelMat = getModelMat(translate, rotate, scale)
const mvpMat = mat4.multiply(mat4.create(), vpMat, modelMat)
return {
perspectiveMat,
viewMat,
modelMat,
mvpMat
}
}
/**
* 绘制
*/
draw(clear: boolean = true) {
const gl = this.glContext
if(clear) {
gl.clearColor(0, 0, 0, 1)
gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT)
}
gl.drawElements(gl.TRIANGLES, this.indexBufferLength, gl.UNSIGNED_BYTE, 0);
}
}
export default WebGLLib
interface GetMVPParams {
perspective?: {
fov?: number,
aspect?: number,
near?: number,
far?: number
},
lookAt?: {
eye?: ReadonlyVec3
center?: ReadonlyVec3
up?: ReadonlyVec3
},
model?: {
translate?: ReadonlyVec3,
rotate?: ReadonlyVec3,
scale?: ReadonlyVec3
}
}
// utils
import { ReadonlyVec3, mat4, quat } from "gl-matrix"
const axis = ['X', 'Y', 'Z']
/**
* 获取model矩阵
* @param translate [x, y, z]平移
* @param rotate 旋转角度
*/
export const getModelMat = (translate: ReadonlyVec3 = [0, 0, 0], rotate: ReadonlyVec3 = [0, 0, 0], scale: ReadonlyVec3 = [1, 1, 1]) => {
const routateQuat = quat.create()
rotate.forEach((r, index) => {
let rad = r * Math.PI / 180
// @ts-ignore
quat[`rotate${axis[index]}`]?.(routateQuat, routateQuat, rad)
})
const modelMat = mat4.fromRotationTranslationScale(mat4.create(), routateQuat, translate, scale)
return modelMat
}
// 主程序
import vertexCode from './vertex.vert'
import fragmentCode from './fragment.frag'
import { mat4, vec3, vec4, glMatrix } from 'gl-matrix'
import { getModelMat } from './utils'
import WebGLLib from './lib'
const output = document.getElementById('output') as HTMLDivElement
const Lib = new WebGLLib(document.getElementById('webgl') as HTMLCanvasElement)
Lib.createProgram('normalCube', vertexCode, fragmentCode)
Lib.createProgram('transparentCube', vertexCode, fragmentCode)
const rotate: vec3 = [0, 0, 0]
// 绘制普通立方体
const drawNormalCube = () => {
Lib.use('normalCube');
const {
modelMat,
mvpMat
} = Lib.getMVP({perspective: {fov: 20},model: {rotate, translate: [-3, 0, 0]}})
Lib.enables([WebGLRenderingContext.DEPTH_TEST])
// 绑定顶点缓冲
Lib.bindVertexBuffer(
// v6----- v5
// /| /|
// v1------v0|
// | | | |
// | |v7---|-|v4
// |/ |/
// v2------v3
new Float32Array([
1.0, 1.0, 1.0,1.0, -1.0, 1.0, 1.0,1.0, -1.0,-1.0, 1.0,1.0, 1.0,-1.0, 1.0,1.0, // v0-v1-v2-v3 front
1.0, 1.0, 1.0,1.0, 1.0,-1.0, 1.0,1.0, 1.0,-1.0,-1.0,1.0, 1.0, 1.0,-1.0,1.0, // v0-v3-v4-v5 right
1.0, 1.0, 1.0,1.0, 1.0, 1.0,-1.0,1.0, -1.0, 1.0,-1.0,1.0, -1.0, 1.0, 1.0,1.0, // v0-v5-v6-v1 up
-1.0, 1.0, 1.0,1.0, -1.0, 1.0,-1.0,1.0, -1.0,-1.0,-1.0,1.0, -1.0,-1.0, 1.0,1.0, // v1-v6-v7-v2 left
-1.0,-1.0,-1.0,1.0, 1.0,-1.0,-1.0,1.0, 1.0,-1.0, 1.0,1.0, -1.0,-1.0, 1.0,1.0, // v7-v4-v3-v2 down
1.0,-1.0,-1.0,1.0, -1.0,-1.0,-1.0,1.0, -1.0, 1.0,-1.0,1.0, 1.0, 1.0,-1.0,1.0, // v4-v7-v6-v5 back
]),
'pos',
4
)
// 绑定颜色缓冲
Lib.bindVertexBuffer(
new Float32Array([
0.4, 0.4, 1.0, 1.0, 0.4, 0.4, 1.0, 1.0, 0.4, 0.4, 1.0, 1.0, 0.4, 0.4, 1.0, 1.0, // v0-v1-v2-v3 前(blue)
0.4, 1.0, 0.4, 1.0, 0.4, 1.0, 0.4, 1.0, 0.4, 1.0, 0.4, 1.0, 0.4, 1.0, 0.4, 1.0, // v0-v3-v4-v5 右(green)
1.0, 0.4, 0.4, 1.0, 1.0, 0.4, 0.4, 1.0, 1.0, 0.4, 0.4, 1.0, 1.0, 0.4, 0.4, 1.0, // v0-v5-v6-v1 上(red)
1.0, 1.0, 0.4, 1.0, 1.0, 1.0, 0.4, 1.0, 1.0, 1.0, 0.4, 1.0, 1.0, 1.0, 0.4, 1.0, // v1-v6-v7-v2 左
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, // v7-v4-v3-v2 下
0.4, 1.0, 1.0, 1.0, 0.4, 1.0, 1.0, 1.0, 0.4, 1.0, 1.0, 1.0, 0.4, 1.0, 1.0, 1.0, // v4-v7-v6-v5 后
]),
'color',
4
)
// 绑定法线向量缓冲
Lib.bindVertexBuffer(
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
]),
'originalNormal',
3
)
// 绑定索引
Lib.bindIndexBuffer(
new Uint8Array([
0, 1, 2, 0, 2, 3, // 前
4, 5, 6, 4, 6, 7, // 右
8, 9, 10, 8, 10, 11, // 上
12, 13, 14, 12, 14, 15, // 左
16, 17, 18, 16, 18, 19, // 下
20, 21, 22, 20, 22, 23, // 后
])
)
// 设置光线
Lib.setUniformValue('lightColor', [1, 1, 1])
// 光线方向
Lib.setUniformValue('lightDirection', [ -4, 4, 5])
// 设置环境光颜色
Lib.setUniformValue('ambientColor', [0.2, 0.2, 0.2])
// 设置mvp矩阵
Lib.setUniformMatrixValue('mvpMat', mvpMat)
Lib.setUniformMatrixValue('modelMat', modelMat)
// modelMat的逆转置
Lib.setUniformMatrixValue('normalMat', mat4.transpose(mat4.create(), mat4.invert(mat4.create(), modelMat)))
Lib.draw()
}
// 绘制透明立方体
const drawTransparentCube = () => {
Lib.use('transparentCube');
const {
modelMat,
mvpMat
} = Lib.getMVP({perspective: {fov: 20},model: {rotate, translate: [4, 0, 0]}})
// 开启混合 深度测试
Lib.enables([WebGLRenderingContext.DEPTH_TEST, WebGLRenderingContext.BLEND])
// 设置混合函数
Lib.glContext.blendFunc(WebGLRenderingContext.SRC_ALPHA,WebGLRenderingContext.ONE_MINUS_SRC_ALPHA)
// 绑定顶点缓冲
Lib.bindVertexBuffer(
// v6----- v5
// /| /|
// v1------v0|
// | | | |
// | |v7---|-|v4
// |/ |/
// v2------v3
new Float32Array([
1.0, 1.0, 1.0,1.0, -1.0, 1.0, 1.0,1.0, -1.0,-1.0, 1.0,1.0, 1.0,-1.0, 1.0,1.0, // v0-v1-v2-v3 front
1.0, 1.0, 1.0,1.0, 1.0,-1.0, 1.0,1.0, 1.0,-1.0,-1.0,1.0, 1.0, 1.0,-1.0,1.0, // v0-v3-v4-v5 right
1.0, 1.0, 1.0,1.0, 1.0, 1.0,-1.0,1.0, -1.0, 1.0,-1.0,1.0, -1.0, 1.0, 1.0,1.0, // v0-v5-v6-v1 up
-1.0, 1.0, 1.0,1.0, -1.0, 1.0,-1.0,1.0, -1.0,-1.0,-1.0,1.0, -1.0,-1.0, 1.0,1.0, // v1-v6-v7-v2 left
-1.0,-1.0,-1.0,1.0, 1.0,-1.0,-1.0,1.0, 1.0,-1.0, 1.0,1.0, -1.0,-1.0, 1.0,1.0, // v7-v4-v3-v2 down
1.0,-1.0,-1.0,1.0, -1.0,-1.0,-1.0,1.0, -1.0, 1.0,-1.0,1.0, 1.0, 1.0,-1.0,1.0, // v4-v7-v6-v5 back
]),
'pos',
4
)
// 绑定颜色缓冲
Lib.bindVertexBuffer(
new Float32Array([
0.4, 0.4, 1.0, 0.5, 0.4, 0.4, 1.0, 0.5, 0.4, 0.4, 1.0, 0.5, 0.4, 0.4, 1.0, 0.5, // v0-v1-v2-v3 前(blue)
0.4, 1.0, 0.4, 0.5, 0.4, 1.0, 0.4, 0.5, 0.4, 1.0, 0.4, 0.5, 0.4, 1.0, 0.4, 0.5, // v0-v3-v4-v5 右(green)
1.0, 0.4, 0.4, 0.5, 1.0, 0.4, 0.4, 0.5, 1.0, 0.4, 0.4, 0.5, 1.0, 0.4, 0.4, 0.5, // v0-v5-v6-v1 上(red)
1.0, 1.0, 0.4, 0.5, 1.0, 1.0, 0.4, 0.5, 1.0, 1.0, 0.4, 0.5, 1.0, 1.0, 0.4, 0.5, // v1-v6-v7-v2 左
1.0, 1.0, 1.0, 0.5, 1.0, 1.0, 1.0, 0.5, 1.0, 1.0, 1.0, 0.5, 1.0, 1.0, 1.0, 0.5, // v7-v4-v3-v2 下
0.4, 1.0, 1.0, 0.5, 0.4, 1.0, 1.0, 0.5, 0.4, 1.0, 1.0, 0.5, 0.4, 1.0, 1.0, 0.5, // v4-v7-v6-v5 后
]),
'color',
4
)
// 绑定法线向量缓冲
Lib.bindVertexBuffer(
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
]),
'originalNormal',
3
)
// 绑定索引
Lib.bindIndexBuffer(
new Uint8Array([
4, 5, 6, 4, 6, 7, // 右
12, 13, 14, 12, 14, 15, // 左
16, 17, 18, 16, 18, 19, // 下
20, 21, 22, 20, 22, 23, // 后
8, 9, 10, 8, 10, 11, // 上
0, 1, 2, 0, 2, 3, // 前
])
)
// 设置光线
Lib.setUniformValue('lightColor', [1, 1, 1])
// 光线方向
Lib.setUniformValue('lightDirection', [ -4, 4, 5])
// 设置环境光颜色
Lib.setUniformValue('ambientColor', [0.2, 0.2, 0.2])
// 设置mvp矩阵
Lib.setUniformMatrixValue('mvpMat', mvpMat)
Lib.setUniformMatrixValue('modelMat', modelMat)
// modelMat的逆转置
Lib.setUniformMatrixValue('normalMat', mat4.transpose(mat4.create(), mat4.invert(mat4.create(), modelMat)))
Lib.glContext.depthMask(false)
Lib.draw(false)
Lib.glContext.depthMask(true)
}
drawNormalCube()
drawTransparentCube()
initEventHandlers()
function initEventHandlers() {
let dragging = false;
let lastX = -1, lastY = -1;
const canvas = Lib.canvas
window.oncontextmenu = (e) => {
e.preventDefault()
}
canvas.addEventListener('mousedown', function(e) {
const {
clientX,
clientY,
button
} = e
// 右键才能拖动
if(button !== 2) return
var rect = this.getBoundingClientRect();
if (rect.left <= clientX && clientX < rect.right && rect.top <= clientY && clientY < rect.bottom) {
lastX = clientX; lastY = clientY;
dragging = true;
}
})
canvas.onmouseup = function(ev) { dragging = false; };
canvas.addEventListener('mousemove', function(e) {
const {
clientX,
clientY
} = e
if (dragging) {
var factor = 400 / canvas.width; // The rotation ratio
var dx = factor * (clientX - lastX);
var dy = factor * (clientY - lastY);
rotate[0] = rotate[0] + dy
rotate[1] = rotate[1] + dx;
drawNormalCube()
drawTransparentCube()
}
lastX = clientX, lastY = clientY;
})
}
4. 另外
改进下操作,可以单独控制每个立方体。
需要复习一下WebGL学习(九)选中物体
// 处理事件的时候需要改进一下
function initEventHandlers() {
// ....
canvas.addEventListener('mousedown', function(e) {
const {
clientX,
clientY,
button
} = e
// 右键才能拖动
if(button !== 2) return
var rect = this.getBoundingClientRect();
if (rect.left <= clientX && clientX < rect.right && rect.top <= clientY && clientY < rect.bottom) {
lastX = clientX; lastY = clientY;
dragging = true;
const {
width,
left,
bottom,
} = Lib.canvas.getBoundingClientRect()
const scale = Lib.canvas.width / width
const res = new Uint8Array(4)
const canvasX = clientX - left
const canvasY = bottom - clientY
// 判断是否点击
Lib.use('normalCube')
Lib.setUniformValue('clicked', [1])
drawNormalCube()
drawTransparentCube(false)
Lib.use('normalCube')
Lib.glContext.readPixels(canvasX * scale, canvasY * scale, 1, 1, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, res)
Lib.setUniformValue('clicked', [0])
drawNormalCube()
drawTransparentCube(false)
Lib.clickedMap.set('normalCube', false)
if(res.slice(0, 3).every(i => !i)) {
Lib.clickedMap.set('normalCube', true)
}
Lib.use('transparentCube')
Lib.setUniformValue('clicked', [1])
drawNormalCube()
drawTransparentCube(false)
Lib.glContext.readPixels(canvasX * scale, canvasY * scale, 1, 1, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, res)
Lib.setUniformValue('clicked', [0])
drawNormalCube()
drawTransparentCube(false)
Lib.clickedMap.set('transparentCube', false)
if(res.slice(0, 3).every(i => !i)) {
Lib.clickedMap.set('transparentCube', true)
}
}
})
canvas.onmouseup = function(ev) { dragging = false; };
canvas.addEventListener('mousemove', function(e) {
const {
clientX,
clientY
} = e
if (dragging) {
var factor = 400 / canvas.width; // The rotation ratio
var dx = factor * (clientX - lastX);
var dy = factor * (clientY - lastY);
// 分别设置旋转
if(Lib.clickedMap.get('normalCube')) {
normalCubeRotate[0] = normalCubeRotate[0] + dy
normalCubeRotate[1] = normalCubeRotate[1] + dx;
}
if(Lib.clickedMap.get('transparentCube')) {
transparentCubeRotate[0] = transparentCubeRotate[0] + dy
transparentCubeRotate[1] = transparentCubeRotate[1] + dx;
}
drawNormalCube()
drawTransparentCube(false)
}
lastX = clientX, lastY = clientY;
})
}