须要理解一下的前置常识(上面是举荐浏览的链接)
GLSL github.com/wshxbqq/GLS…
Shader thebookofshaders.com/01/
矩阵 bk.tw.lvfukeji.com/wiki/%E7%9F…
齐次坐标 bk.tw.lvfukeji.com/wiki/%E9%BD…
最终成果
step1:建设webgl渲染上下文
这个就是简略的获取dom而后获取上下文 (留神下这里因为是画3d所以要开启深度检测)
const canvasDom = document.getElementById('canvas')gl = canvasDom.getContext('webgl')//开启深度检测gl.enable(gl.DEPTH_TEST)
step2:创立顶点着色器与片元着色器
对于着色器shader是一个超级大的话题(举荐看TheBooksOfShader,难堪的是作者没写完)。
大抵能够这么了解:
• 顶点着色器确定了画布上点的地位
• 3d世界中根底的几何图形是三角形,片元着色器代表了区域的表现形式
先看一下webgl的坐标系,z+轴是面向咱们的视角:
上面这段是顶点着色器:
const Vertex = ` attribute vec3 vPosition; void main() { gl_PointSize = 1.0; gl_Position = mat4(1,0, 0, 0 ,0, 0.52, -0.85, 0, 0, 0.85, 0.52, 0, 0, 0, 0,1)*mat4(0.52,0, 0.85, 0 ,0, 1, 0, 0, -0.85, 0, 0.52, 0, 0, 0, 0,1)*vec4(vPosition, 1); } `
attribute : 只能存在于vertex shader中,个别用于保留顶点或法线数据,它能够在数据缓冲区中读取数据。
vec3 vPosition 定义了一个3维向量
因为3d空间一个点(x,y,z)
mat4(1,0, 0, 0 ,0, 0.52, -0.85, 0, 0, 0.85, 0.52, 0, 0, 0, 0,1)是一个齐次矩阵 示意绕y轴旋转45度
mat4(0.52,0, 0.85, 0 ,0, 1, 0, 0, -0.85, 0, 0.52, 0, 0, 0, 0,1)示意绕z轴旋转45度
这样咱们能力看到3d的成果。
上面这个就是编译shader,固定套路记住就好了 (开发中不大会用原生手写webgl)
const vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertexShader, Vertex); gl.compileShader(vertexShader);
上面这段是片元着色器:
const Fragment = ` #ifdef GL_ES precision highp float; #endif void main() { gl_FragColor = vec4(1.0,0,0,1.0); } `
示意的意思是画布上的色彩是红色 vec4(1.0,0,0,1.0) 而后也是固定套路:
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragmentShader, Fragment); gl.compileShader(fragmentShader);
step3:创立一个程序
记住就好,就是调用api
const program = gl.createProgram();
step4:链接程序与着色器
记住就好,就是调用api
gl.attachShader(program, vertexShader);gl.attachShader(program, fragmentShader);gl.linkProgram(program);gl.useProgram(program);
step5:建设缓冲数据
cube是用来获取顶点坐标
剩下的都是套路,api就不开展了,能够去mdn上查阅
//cube是用来获取顶点坐标function cube(size = 1.0) { const h = 0.5 * size; const vertices = [ [-h, -h, -h], [-h, h, -h], [h, h, -h], [h, -h, -h], [-h, -h, h], [-h, h, h], [h, h, h], [h, -h, h], ]; const positions = []; function quad(a, b, c, d, e, f, g, h) { [a, b, c, d, e, f, g, h].forEach((i) => { positions.push(vertices[i]); }); } quad(0, 1, 1, 2, 2, 3, 3, 0); quad(4, 5, 5, 6, 6, 7, 7, 4); quad(1, 2, 2, 6, 6, 5, 5, 1); quad(0, 3, 3, 7, 7, 4, 4, 0); quad(0, 1, 1, 5, 5, 4, 4, 0); quad(3, 7, 7, 6, 6, 2, 2, 3); return { positions}; } const geometry = cube(1.0); console.log(geometry) const points = new Float32Array(geometry.positions.flat()); const bufferId = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, bufferId); gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
step6渲染
const Position = gl.getAttribLocation(program, 'vPosition');//获取顶点着色器中的position变量的地址gl.vertexAttribPointer(Position, 3, gl.FLOAT, false, 0, 0);//给变量设置长度和类型gl.enableVertexAttribArray(Position);//激活这个变量gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)gl.drawArrays(gl.LINES, 0, 48)
gl.drawArrays(gl.LINES, 0, 48)就是渲染的api
webgl中有7种图元 (示意怎么画)
gl.POINTS 孤立点 绘制一系列点
gl.LINES 绘制一系列独自线段。每两个点作为端点,线段之间不连贯
gl.LINE_STRIP 间断线段 绘制一个线条。即,绘制一系列线段,上一点连贯下一点
gl.LINE_LOOP 间断线圈 绘制一个线圈。即,绘制一系列线段,上一点连贯下一点,并且最初一点与第一个点相连
gl.TRIANGLES 孤立三角形
gl.TRIANGLE_STRIP 三角带
gl.TRIANGLE_FAN 三角扇
0,48示意从0取48个点绘制
总结
上述过程就是一个残缺的webgl绘画流程。
残缺的代码
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>3D立方体</title></head><body> <canvas id='canvas' width="800" height="800"></canvas></body><script> // 第一步 创立webgl上下文 const canvasDom = document.getElementById('canvas') gl = canvasDom.getContext('webgl') //开启深度检测 gl.enable(gl.DEPTH_TEST) console.log(gl) // 第二步 创立顶点着色器与片元着色器 const Vertex = ` attribute vec3 vPosition; void main() { gl_PointSize = 1.0; gl_Position = mat4(1,0, 0, 0 ,0, 0.52, -0.85, 0, 0, 0.85, 0.52, 0, 0, 0, 0,1)*mat4(0.52,0, 0.85, 0 ,0, 1, 0, 0, -0.85, 0, 0.52, 0, 0, 0, 0,1)*vec4(vPosition, 1); } ` const vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertexShader, Vertex); gl.compileShader(vertexShader); const Fragment = ` #ifdef GL_ES precision highp float; #endif void main() { gl_FragColor = vec4(1.0,0,0,1.0); } ` const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragmentShader, Fragment); gl.compileShader(fragmentShader); //第三步 创立程序对象 const program = gl.createProgram(); // 第四步 链接程序与着色器 gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); gl.linkProgram(program); gl.useProgram(program); //第五步 建设缓冲数据 function cube(size = 1.0) { const h = 0.5 * size; const vertices = [ [-h, -h, -h], [-h, h, -h], [h, h, -h], [h, -h, -h], [-h, -h, h], [-h, h, h], [h, h, h], [h, -h, h], ]; const positions = []; function quad(a, b, c, d, e, f, g, h) { [a, b, c, d, e, f, g, h].forEach((i) => { positions.push(vertices[i]); }); } quad(0, 1, 1, 2, 2, 3, 3, 0); quad(4, 5, 5, 6, 6, 7, 7, 4); quad(1, 2, 2, 6, 6, 5, 5, 1); quad(0, 3, 3, 7, 7, 4, 4, 0); quad(0, 1, 1, 5, 5, 4, 4, 0); quad(3, 7, 7, 6, 6, 2, 2, 3); return { positions}; } const geometry = cube(1.0); console.log(geometry) const points = new Float32Array(geometry.positions.flat()); const bufferId = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, bufferId); gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW); // 第六步 渲染 const Position = gl.getAttribLocation(program, 'vPosition');//获取顶点着色器中的position变量的地址 gl.vertexAttribPointer(Position, 3, gl.FLOAT, false, 0, 0);//给变量设置长度和类型 gl.enableVertexAttribArray(Position);//激活这个变量 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) gl.drawArrays(gl.LINES, 0, 48)</script></html>
文|alex
关注得物技术,携手走向技术的云端