前言
在3D图形渲染中,雾化是一种用于创建深度感的技术。它模拟了真实世界中的雾气效果,使远距离的物体看起来模糊不清,并且与背景融合在一起,从而增强了场景的真实感。
实现3D中的雾化技术,通常通过在场景中添加一层透明的雾气效果,即雾层。这个雾层可以是线性的或指数的,具体的数学公式可以用来计算每个像素的雾化强度。这种技术使得远距离的物体看起来像是遮盖在一层雾气中,从而增强了场景的深度感。
雾化技术在电影、游戏等领域广泛应用,能够提高场景的视觉效果和真实感。
一、雾化
1.概念
WEBGL中的雾化是一种视觉效果,可以模拟远处物体出现“雾气”或“烟雾”等效果,使场景更加真实。雾化可以让场景中的物体看起来模糊或透明,从而创建出一种深度感和距离感,增强场景的立体感。
WEBGL中的雾化通常使用线性或指数函数来计算出物体的深度,然后根据深度来进行颜色和透明度的调整,从而实现雾化效果。调整透明度的方式有多种,例如将物体的alpha值进行平滑过渡,或者使用特定的shader程序来自定义透明度的计算方式。
在实际的WEBGL应用中,雾化可以被用于增强场景的真实感,同时也可以用于创建特定的氛围和视觉风格。
2.雾化的实现流程
雾化实现是通过某点和视点之间的距离,距离越远雾化程度越⾼。这种雾化也称为线性雾化。某一点的雾化程度也成为雾化因⼦,WebGL中实现雾化的一般流程如下:
-
创建shader:在WebGL中,我们使用着色器(shader)来描述如何绘制一个物体,因此,我们需要创建雾化的shader。在shader中,我们需要定义雾化程度的变量,以及计算每个像素颜色时需要使用的公式等。
-
定义雾化区域:定义哪些区域需要雾化。对于这一步,我们可以依据相机的距离,将远离相机的物体进行雾化处理,或者根据场景中物体的坐标确定需要雾化的区域。
扫描二维码关注公众号,回复: 16101700 查看本文章 -
计算雾化程度:计算每个像素的雾化程度,这个程度可以用雾化公式进行计算,可以使用线性雾化公式或者指数雾化公式等。
-
应用雾化:将计算得到的雾化程度,应用到相应的像素上,最终实现雾化效果。
实现雾化效果时,需要考虑场景中物体的深度,而且雾化程度也可能会随着距离的远近而变化,因此,需要综合考虑这些因素,调整雾化效果。
- 雾化因子计算:雾化因子 = (终点 - 当前点) / (终点 - 起点)
- 物体颜色计算:颜色 = 物体颜色 * 雾化因子 + 雾化颜色 * ( 1 - 雾化因子)
3.案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../lib/index.js"></script>
<style>
* {
margin: 0;
padding: 0;
}
canvas{
margin: 50px auto 0;
display: block;
background: yellow;
}
</style>
</head>
<body>
<canvas id="canvas" width="400" height="400">
此浏览器不支持canvas
</canvas>
</body>
</html>
<script>
const ctx = document.getElementById('canvas')
const gl = ctx.getContext('webgl')
// 创建着色器源码
const VERTEX_SHADER_SOURCE = `
attribute vec4 aPosition;
attribute vec4 aNormal;
varying vec4 vColor;
varying float vDist;
uniform mat4 mat;
void main() {
// 定义点光源的颜色
vec3 uPointLightColor = vec3(1.0,1.0,0.0);
// 点光源的位置
vec3 uPointLightPosition = vec3(-5.2,5.6,5.0);
// 环境光
vec3 uAmbientLightColor = vec3(0.2,0.2,0.2);
// 物体表面的颜色
vec4 aColor = vec4(1.0,0.0,0.0,1.0);
// 顶点的世界坐标
vec4 vertexPosition = mat * aPosition;
// 点光源的方向
vec3 lightDirection = normalize(uPointLightPosition - vec3(vertexPosition));
// 环境反射
vec3 ambient = uAmbientLightColor * vec3(aColor);
// 计算入射角 光线方向和法线方向的点积
float dotDeg = dot(lightDirection, vec3(aNormal));
// 漫反射光的颜色
vec3 diffuseColor = uPointLightColor * vec3(aColor) * dotDeg;
gl_Position = vertexPosition;
vColor = vec4(ambient + diffuseColor, aColor.a);
vDist = gl_Position.w;
}
`; // 顶点着色器
const FRAGMENT_SHADER_SOURCE = `
precision lowp float;
varying vec4 vColor;
varying float vDist;
// 雾化颜色
uniform vec3 uFogColor;
// 起点到终点的距离 第一个参数是起点 第二个是终点
uniform vec2 uFogDist;
void main() {
// 计算雾化因子
float fogFactor = (uFogDist.y - vDist) / (uFogDist.y - uFogDist.x);
// 看到的颜色是什么 物体颜色*雾化因子+雾化颜色*(1-雾化因子)
// mix 线性混合计算 mix(x,y,a) => { x * (1-a) + y * a }
vec3 color = mix(uFogColor, vec3(vColor), fogFactor);
gl_FragColor = vec4(color, vColor.a);
}
`; // 片元着色器
const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)
const aPosition = gl.getAttribLocation(program, 'aPosition');
const aNormal = gl.getAttribLocation(program, 'aNormal');
const mat = gl.getUniformLocation(program, 'mat');
const vertices = new Float32Array([
// 0123
1, 1, 1,
-1, 1, 1,
-1,-1, 1,
1,-1, 1,
// 0345
1, 1, 1,
1,-1, 1,
1,-1,-1,
1, 1,-1,
// 0156
1, 1, 1,
1, 1, -1,
-1, 1,-1,
-1, 1,1,
// 1267
-1, 1, 1,
-1,1, -1,
-1, -1,-1,
-1,-1,1,
// 2347
-1,-1, 1,
1,-1, 1,
1,-1,-1,
-1,-1,-1,
// 4567
1,-1,-1,
1, 1,-1,
-1, 1,-1,
-1,-1,-1,
])
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aPosition)
// 法向量
const normals = 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,
0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,
-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,
1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,
0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,
0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,
])
const normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, normals, gl.STATIC_DRAW);
gl.vertexAttribPointer(aNormal, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aNormal)
const indeces = 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,
])
const indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indeces, gl.STATIC_DRAW);
const start = 0;
const end = 200;
const fogColor = new Float32Array([0.0,0.0,0.0]);
const fogDist = new Float32Array([start, end]);
const uFogColor = gl.getUniformLocation(program, 'uFogColor');
const uFogDist = gl.getUniformLocation(program, 'uFogDist');
gl.uniform3fv(uFogColor, fogColor);
const vm = getViewMatrix(3,3,5,0.0,0.0,0.0,0.0,0.6,0.0);
const perspective = getPerspective(30, ctx.width / ctx.height, 100, 1);
gl.enable(gl.DEPTH_TEST);
gl.uniformMatrix4fv(mat, false, mixMatrix(perspective, vm));
function draw() {
fogDist[1] -= 1;
if (fogDist[1] < start) {
fogDist[1] = end;
}
gl.uniform2fv(uFogDist, fogDist);
gl.clearColor(0.0,0.0,0.0,1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.enable(gl.DEPTH_TEST);
gl.drawElements(gl.TRIANGLES, indeces.length, gl.UNSIGNED_BYTE, 0);
requestAnimationFrame(draw)
}
draw();
</script>