一、水滴滴落简介
利用three.js等技术来实现水珠滴落的效果。本次实验最终将呈现出3D立体感的水珠滴落的成品。
二、相关技术的简介
我们知道,在露珠从一个物体表面滴落的时候,会产生一种粘着的效果。2D平面中,这种粘着效果其实用css滤镜就可以轻松实现。但是到了3D世界,就没那么简单了,这时我们就得依靠光照来实现,其中涉及到了一个关键算法——光线步进(Ray Marching)。
光线步进(Ray Marching):
Ray Marching是一种用于实时场景的快速渲染方法,我的理解是,模拟摄像机位置,根据视椎体的扩张角度,以摄像机位置为原点,进步式发射射线,当射线碰撞到物体之后,返回其深度信息,如果到视椎体的最大距离之前都没有返回,那么可以以此判断该像素点没有对于物体,最后根据返回的信息计算光照。
在光线步进中,整个场景会由一系列的sdf的角度定义。为了找到场景和视线之间的边界,我们会从相机的位置开始,沿着射线,一点一点地移动每个点,每一步都会判断这个点在不在场景的某个表面内部,如果在则完成,表示光线击中了某东西,如果不在则光线继续步进。
光线步进和光线追踪
首先,我们需要知道光线追踪是如何进行的:给相机一个位置eye,在前面放一个网格,从相机的位置发射一束射线ray,穿过网格打在物体上,所成的像的每一个像素对应着网格上的每一个点。
正交相机
摄像机使用orthographic projection(正交投影)来进行投影。
在这种投影模式下,无论物体距离相机距离远或者近,在最终渲染的图片中物体的大小都保持不变。
OrthographicCamera( left : Number, right : Number, top : Number, bottom : Number, near : Number, far : Number )
left — 摄像机视锥体左侧面。
right — 摄像机视锥体右侧面。
top — 摄像机视锥体上侧面。
bottom — 摄像机视锥体下侧面。
near — 摄像机视锥体近端面。
far — 摄像机视锥体远端面。
这些参数一起定义了摄像机的viewing frustum(视锥体)。
rayMarchingVertexShader(顶点着色器)
Vertex Data(顶点数据):OpenGL将所有数据保存到缓存对象当中,正如上节当中的glVertexAttribPointer()函数所做的工作,并调用glDrawArrays()函数请求渲染几何图元
Vertex Shader(顶点着色器):接受在顶点缓存对象中给出的顶点数据,独立处理每个顶点(对于绘制命令传输的每个顶点,OpenGL都会调用一个顶点着色器来处理顶点的相关数据)。这个阶段是必须的
Tessellationj shading stage(细分着色阶段):这个阶段启用之后,会收到来自顶点着色阶段的输出数据,并对收到的顶点进行进一步的处理,它会在OpenGL管线内部生成新的几何体。这是一个可选阶段
Geometry Shader(几何着色器):它会在OpenGL管线内部对所有几何图元进行修改,可以选择输入图元生成更多的几何体,改变几何图元的类型(将三角形转化乘线段之类),或者放弃所有的几何体。这是一个可选阶段
Primitive Setup(图元装配):之前着色阶段处理的都是顶点数据,此外,这些顶点构成几何图元的所有信息也会被传递到OpenGL当中。图元装配阶段将这些顶点与相关的几何图元之间组织起来,准备下一步的剪切和光栅化工作
Culling and Clipping(裁剪和剪切):顶点可能落在视口之外(即我们能够绘制的窗口区域),此时顶点相关的图元会做出改动,保证相关像素不会绘制在视口以外。由OpenGL自动完成
Rasterization(光栅化):光栅化是判断某一部分几何体(点、线或者三角形)所覆盖的屏幕空间。因为屏幕是由一个个的像素点构成的,如果要画一条线,就要判断这条线在哪几个像素点表示。
Fragment Shader(片元着色器):最后一个可以通过编程控制编程控制屏幕上显示颜色的阶段叫做片元着色阶段。这个阶段处理OpenGL光栅化之后生成的独立片元,使用着色器计算片元的最终颜色和它的的深度值。
输入参数介绍:
1.着色器程序(Shader Program,图中没有画出):由 main 申明的一段程序源码或可执行文件,描述在顶点上执行的操作:如坐标变换、计算光照公式产生每个顶点颜色、计算纹理坐标。
2.属性(Attribute):由 vertext array 提供的顶点数据,如空间位置,法向量,纹理坐标以及顶点颜色,属性可以理解为针对每一个顶点的输入数据。属性只在顶点着色器中才有,片元着色器中没有属性。
3.统一值(Uniforms): Uniforms保存由应用程序传递给着色器的只读常量数据。在顶点着色器中,这些数据通常是变换矩阵,光照参数,颜色等。由 uniform 修饰符修饰的变量属于全局变量,该全局性对顶点着色器与片元着色器均可见,也就是说,这两个着色器如果被连接到同一个应用程序中,它们共享同一份 uniform 全局变量集。因此如果在这两个着色器中都声明了同名的 uniform 变量,要保证这对同名变量完全相同:同名+同类型,因为它们实际是同一个变量。
4.采样器(Samplers): 一种特殊的 uniform,用于呈现纹理。sampler 可用于顶点着色器和片元着色器。
输出参数介绍:
1.可变变量(Varying):varying 变量用于存储顶点着色器的输出数据,也存储片元着色器的输入数据。varying 变量会在光栅化处理阶段被线性插值。顶点着色器如果声明了 varying 变量,它必须被传递到片元着色器中才能进一步传递到下一阶段,因此顶点着色器中声明的 varying 变量都应在片元着色器中重新声明为同名同类型的 varying 变量。
gl_Position:在顶点着色器阶段至少应输出位置信息-即内建变量
gl_FrontFacing:为back-face culling stage阶段生成的变量,无论精选是否被禁用,该变量都会生成。
gl_PointSize:点大小。
rayMarchingFragmentShader(片元着色器)
片元着色器的作用是处理由光栅化阶段生成的每个片元,最终计算出每个像素的最终颜色。归根结底,实际上就是数据的集合。这个数据集合包含每一个像素的各个颜色分量和像素透明度的值。
输入参数介绍:
1.着色器程序(Shader program): 由 main
申明的一段程序源码,描述在片元上执行的操作。
2.可变变量(Varyings): 顶点着色器阶段输出的 varying 变量在光栅化阶段被线性插值计算之后输出到片元着色器中作为它的输入,即上图中的 gl_FragCoord,gl_FrontFacing 和 gl_PointCoord。
3.统一值(Uniforms): 用于片元着色器的常量,如雾化参数,纹理参数等。
4.采样器(Samples): 一种特殊的 uniform,用于呈现纹理。
输出参数介绍:
1.gl_FragColor: 在顶点着色器阶段只有唯一的 varying 输出变量-即内建变量gl_FragColor。
matcap贴图
Matcap全称MaterialCapture(材质捕获)
是一种把光照信息存储在纹理,从而省略大量光照计算(只需要采样一张图),就可以实现有光的感觉。
优点是能出效果、非常省。
缺点是光照图是死的,难以使效果与环境产生交互。
适用的情况:比如现在很多手游的展示用场景,基本上场景不会出现太大的光照变化,更多的是人物特效、人物本身的展示,这就可以用到matcap。
法线贴图大家都应该有所了解,法线贴图是通过一张纹理,把法线信息给gpu采样。
菲涅尔公式
用来描述光在不同折射率的介质之间的行为。用公式推导出的光的反射称之为“菲涅尔反射”。
运用于类似水面的效果,在近的地方反射较少,看远的地方反射较多。这种效果称之为菲涅尔效应。
一般运用于水面效果,试想一下你站在湖边,低头看向水里,你会发现近的地方非常清澈见底(反射较少),而看远的地方却倒映着天空(反射较多)。
当你站在湖边,低头看脚下的水,你会发现水是透明的,反射不是特别强烈,如果你抬头看远处的湖面,会发现水不是透明的,反射非常强烈,这就是“菲涅尔效应”。从数学上来讲,就是视线垂直于水平面呈90°,则反射较弱,而视线方向逐渐改变,非垂直于水平面,而是与水平面夹角从90°慢慢减小,反射就越明显。如果你正对着看一个球体,视线聚焦于球心的时候反射较弱,视线越靠近球体边缘反射越强,这个菲涅尔反射效应在现实世界和三维图形中有很广泛的使用。
三、成品展示
四、参考文献
1.https://www.iquilezles.org/www/articles/normalsSDF/normalsSDF.htm
2.https://gist.github.com/yiwenl/3f804e80d0930e34a0b33359259b556c
3.https://www.iquilezles.org/www/articles/smin/smin.htm
4.https://blog.csdn.net/xgangzai/article/details/115451390
五、成品展示连接
链接:https://pan.baidu.com/s/1ngQrgx86AQvIsUN3rfowBQ
提取码:c86r
小组成员:冯浈浈 李淑雅 李亚龙 翟嘉乐 赵泽庚