返回
零基础充分理解WebGL(三)距离场构图法的进一步探索
前端
2023-10-18 17:11:37
在上一篇中,我们深入浅出地了解了距离场构图法的基本原理,为后续的图形绘制奠定了坚实的基础。本篇博文将继续我们的探索之旅,深入探究距离场构图法的更多奥秘,让你领略它绘制复杂图形的强大威力。
距离场构图法的进阶应用
距离场构图法不仅能绘制简单的形状,还能构建更为复杂的几何体。以下是一些进阶应用场景:
- 绘制球体: 将球体视为由无数小球体组成的集合,通过计算每个小球体的距离场值,即可得到整个球体的距离场,进而绘制出平滑的球体形状。
- 绘制地形: 距离场构图法可以模拟自然界的地形,例如山峰和河流。通过将地形的高程数据转换为距离场,可以创建逼真的地形模型。
- 绘制云朵: 云朵具有柔软、蓬松的特点。利用距离场构图法,我们可以模拟云朵的密度分布,从而绘制出逼真的云朵效果。
- 绘制其他复杂形状: 距离场构图法几乎可以绘制任何形状,例如树叶、动物和人物模型,为三维建模提供了广阔的空间。
代码实现示例
以下代码段展示了如何利用距离场构图法绘制球体:
// 定义球体的半径
const radius = 1.0;
// 创建一个距离场纹理
const distanceFieldTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, distanceFieldTexture);
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.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.R32F, width, height, 0, gl.RED, gl.FLOAT, null);
// 填充距离场纹理
for (let x = 0; x < width; x++) {
for (let y = 0; y < height; y++) {
// 计算点 (x, y) 到球心 (width / 2, height / 2) 的距离
const distance = Math.sqrt((x - width / 2) ** 2 + (y - height / 2) ** 2);
// 根据距离计算距离场值
let distanceField = distance - radius;
if (distanceField < 0.0) {
distanceField = 0.0;
}
// 存储距离场值到纹理中
const pixelData = new Float32Array([distanceField]);
gl.texSubImage2D(gl.TEXTURE_2D, 0, x, y, 1, 1, gl.RED, gl.FLOAT, pixelData);
}
}
// 使用距离场纹理渲染球体
const vertexShaderSource = `
attribute vec3 a_position;
void main() {
gl_Position = vec4(a_position, 1.0);
}
`;
const fragmentShaderSource = `
precision mediump float;
uniform sampler2D u_distanceFieldTexture;
uniform vec3 u_lightPosition;
void main() {
vec2 uv = gl_FragCoord.xy / vec2(width, height);
float distance = texture2D(u_distanceFieldTexture, uv).r;
vec3 normal = vec3(0.0);
if (distance > 0.0) {
normal = normalize(vec3(
dFdx(distance),
dFdy(distance),
1.0
));
}
vec3 lightDirection = normalize(u_lightPosition - gl_FragCoord.xyz);
float diffuseIntensity = max(dot(normal, lightDirection), 0.0);
gl_FragColor = vec4(diffuseIntensity, diffuseIntensity, diffuseIntensity, 1.0);
}
`;
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSource);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
console.error(gl.getShaderInfoLog(vertexShader));
}
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSource);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
console.error(gl.getShaderInfoLog(fragmentShader));
}
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error(gl.getProgramInfoLog(program));
}
gl.useProgram(program);
// 获取顶点着色器的属性位置
const positionAttributeLocation = gl.getAttribLocation(program, "a_position");
gl.enableVertexAttribArray(positionAttributeLocation);
// 获取片段着色器的 uniform 位置
const distanceFieldTextureLocation = gl.getUniformLocation(program, "u_distanceFieldTexture");
const lightPositionLocation = gl.getUniformLocation(program, "u_lightPosition");
// 设置 uniform 变量
gl.uniform1i(distanceFieldTextureLocation, 0);
gl.uniform3f(lightPositionLocation, -1.0, 1.0, 1.0);
// 创建顶点缓冲区
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1.0, -1.0, 0.0, 1.0, -1.0, 0.0, -1.0, 1.0, 0.0, 1.0, 1.0, 0.0]), gl.STATIC_DRAW);
// 设置顶点属性
gl.vertexAttribPointer(positionAttributeLocation, 3, gl.FLOAT, false, 0, 0);
// 清除颜色缓冲区
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
// 绑定距离场纹理
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, distanceFieldTexture);
// 绘制球体
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
总结
距离场构图法是一种强大的技术,可以绘制各种复杂的图形。通过理解其原理并熟练运用,你可以在三维建模中如鱼得水,创作出令人惊叹的视觉效果。在下一篇博文中,我们将继续探索 WebGL 的更多奥秘,敬请期待!