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

和得物技术一起走进Web3D的世界(1) 画个立方体吧

得物技术发布于 今天 03:07

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

GLSL github.com/wshxbqq/GLS…

Shader thebookofshaders.com/01/

矩阵 bk.tw.lvfukeji.com/wiki/%E7%9F…

齐次坐标 bk.tw.lvfukeji.com/wiki/%E9%BD…

最终效果

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

step1:建立webgl渲染上下文

这个就是简单的获取dom然后获取上下文 (注意下这里因为是画3d所以要开启深度检测)

const canvasDom = document.getElementById('canvas')

gl = canvasDom.getContext('webgl')

//开启深度检测

gl.enable(gl.DEPTH_TEST)

step2:创建顶点着色器与片元着色器

关于着色器shader是一个超级大的话题(推荐看TheBooksOfShader,尴尬的是作者没写完)。

大致可以这么理解:

• 顶点着色器确定了画布上点的位置

• 3d世界中基础的几何图形是三角形,片元着色器代表了区域的表现形式

先看一下webgl的坐标系,z+轴是面向我们的视角:

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

下面这段是顶点着色器:

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
关注得物技术,携手走向技术的云端

javascripthtml前端webglhtml5plus

阅读 43发布于 今天 03:07

本作品系原创,采用《署名-非商业性使用-禁止演绎 4.0 国际》许可协议

avatar

得物技术

31 声望

4 粉丝

0 条评论

得票时间

avatar

得物技术

31 声望

4 粉丝

宣传栏

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

GLSL github.com/wshxbqq/GLS…

Shader thebookofshaders.com/01/

矩阵 bk.tw.lvfukeji.com/wiki/%E7%9F…

齐次坐标 bk.tw.lvfukeji.com/wiki/%E9%BD…

最终效果

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

step1:建立webgl渲染上下文

这个就是简单的获取dom然后获取上下文 (注意下这里因为是画3d所以要开启深度检测)

const canvasDom = document.getElementById('canvas')

gl = canvasDom.getContext('webgl')

//开启深度检测

gl.enable(gl.DEPTH_TEST)

step2:创建顶点着色器与片元着色器

关于着色器shader是一个超级大的话题(推荐看TheBooksOfShader,尴尬的是作者没写完)。

大致可以这么理解:

• 顶点着色器确定了画布上点的位置

• 3d世界中基础的几何图形是三角形,片元着色器代表了区域的表现形式

先看一下webgl的坐标系,z+轴是面向我们的视角:

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

下面这段是顶点着色器:

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
关注得物技术,携手走向技术的云端

以上是 【JS】和得物技术一起走进Web3D的世界(1) 画个立方体吧 的全部内容, 来源链接: utcz.com/a/114536.html

回到顶部