赞
踩
体渲染的东西也看了一段时间了,这里结合Three.js中体积云的例子,实现shdertoy中的一个效果,先放效果图。
Fire2 (shadertoy.com), 这里是参考的效果,可以自行参看源码。
传统建模方式,可以理解为表面建模,通过构建物体外表面,在三维中展示实际物体。相对的,体渲染是从三维数据中生成图像,典型的例子就是医疗上的CT。本文中不涉及体渲染中的光学模型,仅是对数据进行采样,上色。同时简化计算,使用的几何体为圆球,当然也可以换成立方体,计算方式不会复杂,之后附带立方体和球体的射线检测。
体渲染在表现自然现象,云、雾、火等,相对于表面建模,或者贴图有很大优势。最大的不同就是,实心的,当然效果也好太多了。
本次主要要渲染动态更新的体数据,就不需要提前生成体数据了。通过fragmentshader来对采样的射线进行噪声处理,达到动画效果。
噪声函数相关的内容,可以自行搜索,这里贴一个IQ大神的博客地址。Inigo Quilez :: fractals, computer graphics, mathematics, shaders, demoscene and more (iquilezles.org)。
文中涉及的噪声,分型布朗都是从shadertoy获取,代码仅作适当说明。
先解释一下AABB,Axis-Aligned Bounding Box, 轴对称包围盒。我们在放置立方体时,立方体各边都同三个坐标轴平行,可以极大简化计算。如下图,计算射线 o d od od进入一组平面(可以理解为立方体的两个对立面),计算进入和射出的位置时,可以只采用对应轴的分量即可。如下图右侧的公式。
假设平面P垂直于X轴,则计算过程可以仅考虑x方向的分量。图中 p x ′ p'_x px′为平面p在x轴处的值, o x , d x o_x, d_x ox,dx分别为:射线原点的x值,射线朝向 d i r dir dir在x轴的分量(射线朝向为单位向量时,可以理解为,射线进入出去两个平面的时间)。
球体就是射线方程同球面方程,利用求根公式解方程组了,不再介绍。
基础内容介绍完,先看一下Three提供的体渲染的例子。场景中主要包括天空盒,和承载体渲染结果的立方体。这些不做说明,重点看一下渲染使用的着色器。
渲染立方体的材质要使用RawShaderMaterial
,采用自定义的着色器。
vertexshader中主要处理相机位置,和相机朝向,用于传入到fragment中。
Three中相机位置为世界坐标,通过模型变换的逆矩阵,将相机位置换算到模型本地坐标中。模型在本地坐标中处于原点位置,可以简化计算。相机朝向通过顶点位置减相机的本地坐标即可得到。
in vec3 position; uniform mat4 modelMatrix; //模型本地坐标系,转换到世界坐标系 uniform mat4 modelViewMatrix; //模型世界坐标系,转换到相机坐标系空间 uniform mat4 projectionMatrix; //投影矩阵 uniform vec3 cameraPos; //相机位置 out vec3 vOrigin; out vec3 vDirect void main() { //相机空间坐标 vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); //相机在模型本地坐标的位置 vOrigin = vec3( inverse( modelMatrix ) * vec4( cameraPos, 1.0 ) ).xyz; //相机在模型本地坐标的朝向 vDirection = position - vOrigin; gl_Position = projectionMatrix * mvPosition; }
相机位置作为射线原点,相机朝向作为射线方向,这条射线将参与后续的体数据的采样。
代码中的hitBox
即为上面提到的AABB方法。得到的vec2,其中x为最近,y为最远。
void main(){ vec3 rayDir = normalize( vDirection ); vec2 bounds = hitBox( vOrigin, rayDir ); // 丢弃立方体外的像素 if ( bounds.x > bounds.y ) discard; // 当相机位置在立方体内部时,x为负值,射线反向不需要采样,设置为0,即从内部开始采样 bounds.x = max( bounds.x, 0.0 ); // p为射线第一次进入立方体的位置 vec3 p = vOrigin + bounds.x * rayDir; vec3 inc = 1.0 / abs( rayDir ); float delta = min( inc.x, min( inc.y, inc.z ) ); delta /= steps; // Nice little seed from // https://blog.demofox.org/2020/05/25/casual-shadertoy-path-tracing-1-basic-camera-diffuse-emissive/ uint seed = uint( gl_FragCoord.x ) * uint( 1973 ) + uint( gl_FragCoord.y ) * uint( 9277 ) + uint( frame ) * uint( 26699 ); vec3 size = vec3( textureSize( map, 0 ) ); float randNum = randomFloat( seed ) * 2.0 - 1.0; // 对开始位置进行偏移,可能是为了避免射线在表面这种临界条件吧 p += rayDir * randNum * ( 1.0 / size ); vec4 ac = vec4( base, 0.0 ); // 从射线进入,到射线穿过立方体,依次采样 for ( float t = bounds.x; t < bounds.y; t += delta ) { float d = sample1( p + 0.5 ); d = smoothstep( threshold - range, threshold + range, d ) * opacity; float col = shading( p + 0.5 ) * 3.0 + ( ( p.x + p.y ) * 0.25 ) + 0.2; ac.rgb += ( 1.0 - ac.a ) * d * col; ac.a += ( 1.0 - ac.a ) * d; // 采样颜色累积到接近不透明时,停止采样 if ( ac.a >= 0.95 ) break; p += rayDir * delta; } color = ac; if ( color.a == 0.0 ) discard; }
main
方法中的shading
可以理解为上色的过程。
以上了解了体渲染基本流程后,开始修改片元着色器以期实现shadertoy中的效果。
首先,shadertoy中的造型是基于球体做的,我们修改几何体为球体。添加hitSphere
方法,t0,t1依次为进入和出去的时间。
vec2 hitSphere(vec3 origin,vec3 dir){
float b=dot(dir,origin);
float c=dot(origin,origin)-_SphereRadius*_SphereRadius;
float t0=-b-sqrt(b*b-c);
float t1=-b+sqrt(b*b-c);
t0=max(t0,0.);
return vec2(t0,t1);
}
由于我们需要在shader中实现球体的绘制,因此需要通过射线原点到球体的距离来绘制球体。这里类似于cloud页面中的步进采样。
vec4 rayMarch(vec3 rayOrigin,vec3 rayStep,out vec3 pos)
{
vec4 sum=vec4(0.,0.,0.,0.);
pos=rayOrigin;
for(int i=0;i<_VolumeSteps;i++)
{
vec4 col=volumeFunc(pos);
col.a*=_Density;
col.rgb*=col.a;
sum=sum+col*(1.0-sum.a);
pos+=rayStep;
}
return sum;
}
同时为了简化计算,射线累计的采样次数都为固定值,不再单独计算。
void main(){ vec3 rayDir = normalize( vDirection ); vec2 bounds=hitSphere(vOrigin,rayDir); if(bounds.y<0.) discard; vec3 hitPos; //射线第一次进入球的位置 vec3 p=vOrigin+bounds.x*rayDir; vec4 col=rayMarch(p,rayDir*_StepSize,hitPos); color = col; if ( color.a == 0.0 ) discard; }
当rayMarch
中的volumeFunc
直接返回射线到球面的距离时,你将能得到非常光滑的球。
接下来就是对这个球进行造型了,噪声函数很多,可以自行测试,添加噪声函数后,可能得到如下,实时计算的噪声,在GTX1650显卡上,还是很流畅的,再低一些就不好使了,卡成PPT:
这个球还是太规则,添加fbm(分形布朗)后,增加不规则程度,即可得到如下:
最后贴一个不同的着色效果。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。