WebGL 水波及焦散(刻蚀)的渲染总结

这个场景中有几个物体:水的三角网(waterGeometry)、水地面图形(floorGeometry)、鲨鱼(shark)、两块石头(rock1、rock2)、水草植物(plant)、天空盒(skybox)

现在要制作焦散,就要回到上面说到的理论了,首先要计算出折射光线与物体的交点。如果按照纯物理方式来计算,这个方法计算量非常大。所以想要一种简单有效,虽然不符合物理规律但是能够快速模拟的效果。想想阴影不就是利用阴影贴图的原理,从光源位置看场景中的物体,先得到一张深度贴图,然后在实际渲染时,比较每个像素的深度在光源相机坐标系下的深度来判定是否在阴影中。

这里其实利用了类似的思路,先渲染一张环境纹理。在纹理中渲染水下环境,然后使用该纹理来计算光线与环境之间的交点。除了渲染片段深度之外,还渲染了环境图中的片段位置,RGB 信道存储 XYZ 位置,alpha 信道存储深度:

先对照代码来看环境部分,这里是把上面说的物体都加入到了环境中去:

这里的顺序也注意一下,environmentMap没有添加到scene中,他是自己单独渲染到fbo中。

environment顺序在前,water顺序在后。

设置完初始环境之后,给水面加几个小的震动模拟水面波动效果

接着进入主渲染逻辑

先看这部分,挺麻烦的。

一、根据drop生成波动纹理,将波动纹理传递给water作为渲染用。


drop方面涉及的知识点看另一篇文章,这里不赘述

文档:水波渲染demo.note

链接:http://note.youdao.com/noteshare?id=1697c823a2e1270dc6c5bd092252c2c4&sub=1641A27DB586489B8A5A97FEFB3C167A

二、生成上文所说的环境纹理,RGB 信道存储 XYZ 位置,alpha 信道存储深度:


需要注意这里存贮的坐标是世界坐标系,所以这里的纹理也应该是浮点纹理。

顶点着色器:

片元着色器:

image.png

现在将水下环境渲染到了纹理中,然后使用该纹理来计算光线与环境之间的交点

三、渲染得到纹理之后将水波的waterSimulation纹理和环境纹理传递给焦散渲染成焦散纹理


上文说到要计算焦散,先要计算光线与环境的交点。这里的方式如下:

  1. 计算光纤与水面的交点(如果是逐顶点计算,那么每个方格顶点就是交点;如果是逐像素计算需要计算每个像素对应的水面点位置,其实也不难)

  2. 计算光线在水内部的折射光线(知道光线向量、水面法线向量,glsl有内置函数来计算)

  3. 从当前位置沿折射射线方向移动环境图纹理的一个像素(这里是不太好理解,射线是三维向量,纹理是二维图片,这里利用了一种方式将射线方向映射到二维纹理中)

  4. 将存储在当前环境纹理像素中的环境深度与当前深度进行比较。如果环境深度大于当前深度,则意味着我们需要走得更远,因此我们再次运行步骤 3。如果环境深度小于当前深度,则意味着光线在您从中读取的位置击中了环境

  5. 找到交点之后利用这篇文章中的技术来计算焦散强度,生成焦散纹理(https://medium.com/@evanwallace/rendering-realtime-caustics-in-webgl-2a99a29a0b2c),这个纹理包含3D空间中每个点焦散光照强度信息。

  6. 进行最后的环境渲染时,从焦散纹理中读取光照强度信息来进行渲染

下面进行焦散渲染的着色器处理解析

顶点着色器:

片元着色器:

这里片元着色器的那两个面积比率,就是对应开头图片中光线的汇合和散开现象,可以看到当newArea的面积越小强度越大,面积越大强度越低,这也比较符合认知。

四、根据焦散强度渲染水下环境


下面将焦散纹理传递给environment

environment中有几个物体:水的三角网(waterGeometry)、水地面图形(floorGeometry)、鲨鱼(shark)、两块石头(rock1、rock2)、水草植物(plant)、天空盒(skybox)

下面部分先将水波设置为不可见,然后渲染整体场景

来看下水下environment的顶点着色器和片元着色器

这里的positon是environment中的几个模型的顶点坐标

顶点着色器:

文末

篇幅有限没有列举更多的前端面试题,小编把整理的前端大厂面试题PDF分享出来,一共有269页

  • 23
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于WebGL的纹理映射与光照渲染WebGL中常用的图形渲染技术之一。纹理映射是将一张图片贴到一个三维模型表面上,使得模型表面呈现出图片的样子。光照渲染是通过计算光线在三维模型表面上的反射和折射,来模拟真实世界中的光照效果。下面是一个基于WebGL的纹理映射与光照渲染的例子: ```html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>WebGL Texture Mapping and Lighting</title> <style> canvas { border: 1px solid black; } </style> </head> <body> <canvas id="myCanvas" width="400" height="400"></canvas> <script> var gl; var shaderProgram; var vertexBuffer; var indexBuffer; var texture; var textureImage; var angle = 0; function initGL(canvas) { try { gl = canvas.getContext("webgl"); gl.viewportWidth = canvas.width; gl.viewportHeight = canvas.height; } catch (e) { console.log(e); } if (!gl) { console.log("Could not initialise WebGL"); } } function initShaders() { var vertexShaderSource = ` attribute vec3 aVertexPosition; attribute vec2 aTextureCoord; attribute vec3 aVertexNormal; uniform mat4 uMVMatrix; uniform mat4 uPMatrix; uniform mat4 uNMatrix; varying vec2 vTextureCoord; varying vec3 vTransformedNormal; varying vec4 vPosition; void main(void) { vPosition = uMVMatrix * vec4(aVertexPosition, 1.0); gl_Position = uPMatrix * vPosition; vTextureCoord = aTextureCoord; vTransformedNormal = vec3(uNMatrix * vec4(aVertexNormal, 1.0)); } `; var fragmentShaderSource = ` precision mediump float; varying vec2 vTextureCoord; varying vec3 vTransformedNormal; varying vec4 vPosition; uniform sampler2D uSampler; uniform vec3 uAmbientColor; uniform vec3 uLightingDirection; uniform vec3 uDirectionalColor; void main(void) { vec3 ambientLight = uAmbientColor; vec3 directionalLightColor = vec3(0.0, 0.0, 0.0); vec3 directionalVector = normalize(uLightingDirection); float directional = max(dot(vTransformedNormal, directionalVector), 0.0); if (directional > 0.0) { directionalLightColor = uDirectionalColor * directional; } vec4 textureColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t)); gl_FragColor = vec4(textureColor.rgb * (ambientLight + directionalLightColor), textureColor.a); } `; var vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertexShader, vertexShaderSource); gl.compileShader(vertexShader); if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) { console.log(gl.getShaderInfoLog(vertexShader)); } var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragmentShader, fragmentShaderSource); gl.compileShader(fragmentShader); if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) { console.log(gl.getShaderInfoLog(fragmentShader)); } shaderProgram = gl.createProgram(); gl.attachShader(shaderProgram, vertexShader); gl.attachShader(shaderProgram, fragmentShader); gl.linkProgram(shaderProgram); if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { console.log(gl.getProgramInfoLog(shaderProgram)); } gl.useProgram(shaderProgram); shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); shaderProgram.vertexNormalAttribute = gl.getAttribLocation(shaderProgram, "aVertexNormal"); gl.enableVertexAttribArray(shaderProgram.vertexNormalAttribute); shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix"); shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); shaderProgram.nMatrixUniform = gl.getUniformLocation(shaderProgram, "uNMatrix"); shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); shaderProgram.ambientColorUniform = gl.getUniformLocation(shaderProgram, "uAmbientColor"); shaderProgram.lightingDirectionUniform = gl.getUniformLocation(shaderProgram, "uLightingDirection"); shaderProgram.directionalColorUniform = gl.getUniformLocation(shaderProgram, "uDirectionalColor"); } function initBuffers() { var vertices = [ // Front face -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, // Back face -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, // Top face -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, // Bottom face -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, // Right face 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, // Left face -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0 ]; var textureCoords = [ // Front face 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, // Back face 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, // Top face 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, // Bottom face 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, // Right face 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, // Left face 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0 ]; var normals = [ // Front face 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, // Back face 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, // Top face 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, // Bottom face 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, // Right face 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, // Left face -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0 ]; var indices = [ 0, 1, 2, 0, 2, 3, // Front face 4, 5, 6, 4, 6, 7, // Back face 8, 9, 10, 8, 10, 11, // Top face 12, 13, 14, 12, 14, 15, // Bottom face 16, 17, 18, 16, 18, 19, // Right face 20, 21, 22, 20, 22, 23 // Left face ]; vertexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); vertexBuffer.itemSize = 3; vertexBuffer.numItems = 24; indexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW); indexBuffer.numItems = 36; textureCoordsBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordsBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW); textureCoordsBuffer.itemSize = 2; textureCoordsBuffer.numItems = 24; normalBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normals), gl.STATIC_DRAW); normalBuffer.itemSize = 3; normalBuffer.numItems = 24; } function initTexture() { texture = gl.createTexture(); textureImage = new Image(); textureImage.onload = function() { handleLoadedTexture(texture, textureImage); } textureImage.src = "texture.png"; } function handleLoadedTexture(texture, textureImage) { gl.bindTexture(gl.TEXTURE_2D, texture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textureImage); gl.texParameteri(gl.TEXTURE_2D, gl

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值