写点什么

【得物技术】走进 Web3D 的世界 (1) 画个立方体吧

用户头像
得物技术
关注
发布于: 2021 年 02 月 10 日
【得物技术】走进Web3D的世界(1) 画个立方体吧

需要了解一下的前置知识(下面是推荐阅读的链接)

GLSLgithub.com/wshxbqq/GLS…

Shaderthebookofshaders.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

关注得物技术,携手走向技术的云端


发布于: 2021 年 02 月 10 日阅读数: 17
用户头像

得物技术

关注

得物APP技术部 2019.11.13 加入

关注微信公众号「得物技术」

评论

发布
暂无评论
【得物技术】走进Web3D的世界(1) 画个立方体吧