使用 WebGL 创建实时渲染的 3D 场景

算法架构师 2021-08-13 ⋅ 12 阅读

WebGL 是一种基于 JavaScript 的图形 API,可以在网页中使用 GPU 加速进行 2D 和 3D 图形渲染。它可以与 HTML5 的 canvas 元素集成,为开发者提供了在浏览器中渲染实时和交互式的 3D 场景的能力。本文将介绍如何使用 WebGL 进行实时渲染的 3D 场景,并涉及到着色器编程、光照模型和纹理映射等内容。

环境搭建

首先,我们需要确保我们的浏览器支持 WebGL。大多数现代浏览器都已经内置了对 WebGL 的支持,可以使用 WebGLRenderingContext 对象进行相关的编程。

3D 场景基本概念

在开始渲染 3D 场景之前,我们需要了解一些基本的概念。

顶点着色器

顶点着色器是执行在 GPU 上的一段代码,用于将传入的顶点坐标转换为屏幕坐标。

片元着色器

片元着色器是执行在 GPU 上的一段代码,用于计算每个像素的颜色。

光照模型

光照模型用于模拟光线在 3D 场景中的传播和反射。常见的光照模型包括 Lambert、Phong 和 Blinn-Phong 等。

纹理映射

纹理映射是将图像或纹理贴图映射到 3D 物体表面上,以增强渲染效果。

创建 WebGL 上下文

在 HTML 文档中,我们可以通过创建一个 <canvas> 元素以获取 WebGL 上下文。

<canvas id="canvas"></canvas>

然后,在 JavaScript 中获取 WebGL 上下文并设置画布大小。

const canvas = document.getElementById("canvas");
const gl = canvas.getContext("webgl");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

编写着色器程序

对于顶点着色器和片元着色器,我们需要编写 GLSL(OpenGL Shading Language)代码并上传到 GPU 上。

顶点着色器

顶点着色器的代码如下所示:

// attribute 变量用于接收从 CPU 传入的数据
attribute vec4 a_position;
attribute vec3 a_normal;
attribute vec2 a_texCoord;

// uniform 变量用于接收从 CPU 传入的全局数据
uniform mat4 u_transform;
uniform mat4 u_projection;
uniform mat4 u_view;

varying vec3 v_normal;
varying vec2 v_texCoord;

void main() {
    gl_Position = u_projection * u_view * u_transform * a_position;
    v_normal = a_normal;
    v_texCoord = a_texCoord;
}

片元着色器

片元着色器的代码如下所示:

precision mediump float;

uniform vec3 u_lightDir;
uniform sampler2D u_texture;

varying vec3 v_normal;
varying vec2 v_texCoord;

void main() {
    vec3 normal = normalize(v_normal);
    vec3 lightDir = normalize(u_lightDir);

    float lightIntensity = max(dot(normal, lightDir), 0.0);
    vec4 texColor = texture2D(u_texture, v_texCoord);

    gl_FragColor = vec4(texColor.rgb * lightIntensity, texColor.a);
}

渲染 3D 场景

接下来,我们需要设置 WebGL 上下文的顶点数据,创建并连接顶点着色器和片元着色器,并渲染场景。

// 设置顶点数据
const vertices = [
    // 顶点坐标, 法向量, 纹理坐标
    // ...
];

const positions = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positions);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

// 创建并连接顶点着色器和片元着色器
const program = createShaderProgram(gl, vertexShaderSource, fragmentShaderSource);
gl.useProgram(program);

const location = gl.getAttribLocation(program, "a_position");
gl.vertexAttribPointer(location, 3, gl.FLOAT, false, 32, 0);
gl.enableVertexAttribArray(location);

// 渲染场景
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

gl.enable(gl.DEPTH_TEST);

gl.uniform3fv(gl.getUniformLocation(program, "u_lightDir"), [0, 0, -1]);
gl.uniformMatrix4fv(gl.getUniformLocation(program, "u_transform"), false, transformMatrix);
gl.uniformMatrix4fv(gl.getUniformLocation(program, "u_projection"), false, projectionMatrix);
gl.uniformMatrix4fv(gl.getUniformLocation(program, "u_view"), false, viewMatrix);

gl.drawArrays(gl.TRIANGLES, 0, 36);

纹理映射

要实现纹理映射,我们需要先创建纹理对象,加载纹理图像,并传递给片元着色器。

const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);

gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);

gl.uniform1i(gl.getUniformLocation(program, "u_texture"), 0);

光照模型

实现光照模型需要考虑光源的位置和光线的传播。需要计算光照强度,将其乘以纹理颜色。

结语

本文介绍了如何使用 WebGL 创建实时渲染的 3D 场景。我们了解了顶点着色器和片元着色器的基本概念,学习了如何编写着色器程序,并涉及了光照模型和纹理映射等内容。希望这篇博客能帮助你入门 WebGL,并在开发中实现出色的 3D 场景渲染效果。

参考文献:


全部评论: 0

    我有话说: