每日筆記

SDF 3D 参考手册

SDF 3D intersect 参考手册

SDF 2D 参考手册

SDF 2D intersect参考手册

2023年

2月4日

每日閲讀:

每日shadertoy阅读:

在animateTentacle和impulse系列函数里,重点展现了利用数学二次和幂次曲线模拟飞碟的逃逸和章鱼触手的攻击运动和形态,其余部分主要是一个godray的简单march,只有视线方向的transmmitance,没有光线方向的阴影积分 困惑点:animateTentacle里旋转触手的一个mat2,p=(mat2*p.yz).zxy, 并没有沿着触手的方向用xy平面上做2d旋转。没有进一步测试,猜测是旋转法线需要转置?

展示了trilinearNoiseDerivative的插值,得到任意一个噪声贴图的c1连续的噪声和坐标系(normal ,binormal连续),softProjLight一个简单的soft conelight,GDC2007_RealtimeAtmoFxInGamesRev.ppt p17 几句代码的fog

可以漫游的宇宙,集合了大量天体的绘制,此作品的重点是采用不同mipmap下voxel dda的步进处理大规模大尺度时的raymarch,对于天体的绘制会有一个缩短raymarch step的fast版来绘制lod部分,通过天体所在voxel形成的id决定天体的位置, 并使用distantrayPoint检测包围盒碰撞,加速raymarch的速度。丰富的天体合集中漫游是此作品的亮点, 颜色使用的blackbody算法,很好的宇宙调色版。

此作品重点是derivative的噪聲山體,分为geometry和fragment版,step数目不同。沿著視綫trace 3dnoise volume 密度產生wind并和距離霧結合。

每日操练:

想法: 创建一个raymarch平台,画出坐标系,march一个方块体,用于后面的voxel demo

图片:

2月5日

來自 http://www.decarpentier.nl/scape-procedural-extensions 的幾個自然景觀的噪聲swissTurbulence,jordanTurbulence, 展示噪聲在自然風貌生成中的作用,rock和sea做融合,並獲取高度差值用於對shore進行着色。

po函數是轉換成長度和角度的vec2坐標系,ca函數為轉換回普通笛卡爾坐標系,主題函數利用abs二分空間並迭代20次,並利用min叠加繪製sdf綫函數ln,完成對分型樹的繪製。此類分形繪製算法的特點是利用諸如abs,mod之類的函數對空間進行摺叠再進行sdf march.

富含較多實時渲染技巧的風景,水下的藍色利用displacement noise和插值著色的河床,水的著色只使用了depth和fresnal在岩石和水之間插值再通過視距z和遠處的天空色插值,沙灘也是noise鑄型,沙灘和岩石着色只有一個太陽光AO和兩個不同角度的cone法綫ao相乘。 一個簡單的dot的lensflare,水面的marching step數目減少,最後gamma矯正。此作品重點在於通過簡單的插值和豐富的顔色技巧來構建一個逼真的海面和岩石的實時光照。但是速度還是不夠,ao用的太多加上水面反射,marching算法簡單。

iq大神的動畫march作品展示,同时也是6个小时内直播完成的在线livecoding作品之一,此作品的亮點是利用floor,fract對時間進行分段切片,小人的身體部分隨著時間片段的循環進行擠壓變形,這部分的變形曲綫是利用擬合的時間曲綫的導數和法向構成的空間對形體變形動畫進行塑造。 本作包含大量的曲綫函數的設計,通過這些函數對sdf形體進行塑形,對動畫進行變形塑性,精湛絕倫的數學雕刻本領令人向往,基本的數學雕刻也應該是任何一個優秀Tec Artister應具備技能。

2月7日

簡單除z得到透視后的頂點,在屏幕空間對頂點做line sdf。

voronoi 噪聲演示,有注釋,通俗易懂,簡單來説,通過fract和floor,把屏幕分割成格子,每個格子有唯一id產生的hash計算出格子中的voronoi種子點。,遍歷計算一個像素所在周圍8個格子中最近的種子點,並同時返回距離,這樣我們就得到了voronoi每個種子點的sdf圖。最後進行繪製

單buffer的voronoi tracking的演示,把bufferA分成左右兩部分,左邊是包含位置和速度的particl buff,右邊是voronoi buff,其中channels vec4保存4個按到當前距離排序的近鄰的particl ID(實際只用了最近的前兩個particl), 通過insert_sort把當前位置四個方向的近鄰和近鄰的近鄰按距離做插入排序, 得到一個新的按距離排序的vec4近鄰,其中每一幀都會隨機一下voronoi buff 像素到particl id的映射,同時保證每一幀的所有映射不會碰撞(不同voronoi buff中的位置產生相同particle id的狀況)。這樣最終voronoi buff中的vec4近鄰數據排序成了真正的近鄰。

基於voronoi-tracking的sph粒子流體運算的演示。這個vor-tracking的框架相對成熟,把屏幕空間打(4,2)大小的格子,(x,0)第一行是particle的位置速度壓力等sph物理信息,(x,1)第二行是4個方向的neighbors id以及neighbor neighbors的id, 通過fract floor來存取 和計算對應的particle的物理信息和neighbors ID的排序,避免了循環遍歷neighbors同時cache 友好,值得注意的是利用(x,1)作爲四個方向的編碼,對插入排序neighbors進行過濾加速的部分。第二個bufferB通過為每一個像素搜索最近的id並保存得到一張粒子追蹤信息用於繪製,整個過程按cross_distribution的8個方向獲取對應的ID,隨機調整到ID的映射,迭代找出最近的 ID(和BufferA的Neighbors迭代算法類似) 最後Buffc根據BufferB和BufferA的物理信息進行繪製(通過和前一幀插值得到的拖尾),Image直接blit。

3D版本的基於vor-tracking的粒子繪製。整個框架和20200720~130356類似,代碼結構更清晰,buffA負責Particle的物理和neighbors的Update,BufferB是一個voronoi particle tracking,,BufferC是GBuffer 查詢voronoi particle bufferB 對鄰近的particle做球形碰撞檢測,保存z,normal,uv等信息,bufferD是Voronoi light tracing buffer Image 則負責渲染。其中亮點部分,一個是3D版的particle /light tracking 用的是屏幕空間到自己的距離以及粒子的Z深度因子做insert_sort排序的Key。另一個亮點是使用了屏幕空間dda做occ提升渲染效果,最終的渲染通過查詢light tracking buffer獲得當前像素鄰近的lights對Gbuffer進行光照著色。

展示了環形摺叠的技巧,p6m_symmetry:6分對稱摺叠,p2mm_symmetry:abs四分對稱摺叠,這兩個空間對稱摺叠函數的運用,便捷的sincos_colormap調色板, 其中p6m_symmetry的原理是 先四分對稱摺叠UV再旋轉60度后再四分對稱摺叠,重複這個步驟兩次得到6分空間對稱摺叠。

2月8日

3d voxel dda 的例子,bunchless的算法,利用floor和fract獲得步進過程中的整型的當前格子位置和格子中的fract分量位置,通過當前格子位置查詢模擬voxel buffer的隨機函數獲取格子狀態,空格子和滿格子(葉子節點)則繼續步進,其中滿格子條件中使用新的整形起點和分量位置做物體sdf的raymarching,如果是未占滿的格子(有子節點)則需要翻倍dda步進中的size,計算新的整形起點和分量位置,通過分量位置獲得下一級LOD的格子 並查詢voxel buff隨機函數獲得狀態。 重複整個過程完成DDA,最後整個passthrough步進分爲兩部分,dda的整形步進,和用於繪製voxel方塊的raymarch步進,dda步進可以看作獲取voxel級別步進單位對最終的raymarch步進進行加速。

一個利用textureLod獲得2Dvoxel信息,做Quadtree dda 的例子,注釋清晰。但textureLod在本環境中適配出現問題,沒有獲得正確的效果,另外此例子中2dVoxel信息只有lod信息,沒有voxel的子節點信息,因此整個步進算法比較簡單。

2月9日

这个demo充分展示了repetition在sdf中的各种技巧,球体堆积的地面事实上在下沉,除了水平面xz的球体是repetition以外,每一个球体为单位的竖轴Y上的球体也是repetition,竖轴上有三个球其中要保证两个球体堆积,最上面一个球体产生下落效果,每个竖轴运动时间的起始位置都是由球体所在平面格子的hash ID来决定,其中需要对时间进行fract/floor分段切片来绘制三个球体。 整个过程是对floor,fract,hash组合的巧妙运用,加上空间的适当旋转地面的波浪运动,给人一种完全随机的错觉。包含repetition的sdf稍显复杂,又要绘制normal和sss都用到此sdf,因此并没有想象中的高帧率。如果能用voxel表示,就能准确获得球体位置,使用碰撞检测方式进行绘制获得normal信息从而提高绘制,但那样的话repetition似乎也没必要了:)

这个demo展示了基于desity的raymarching以及下雨效果。基于desity的volume raymarching基本原理是视线方向对每一层的影子和光照进行alphablend累加,blend因子a通道由当前层dessity决定,而每一层累加的颜色来自于从当前层位置对光照方向进行desity积分获得的影子和光照。这个demo的亮点是融合了雨滴玻璃窗的效果。雨滴的渲染原理是屏幕中打格子获得id,用id生成hash对格子中的雨滴形状,速度等进行随机化,由于雨滴下落会超过格子,因此整个格子也是随时间下移,最后雨滴由三层不同状态根据时间插值获得。 雨滴效果中值得注意的用在雨滴动态中的几个随机技巧wiggle算法,saw函数。另外此作中雨滴normal的计算使用左右两个雨滴map的插值来计算,性能不高。

这个demo展示了lod层次细节的delauny三角网格生成。整个算法基于jump flood(jfa),一种被广泛用于距离场生成的实时算法(非真正的距离场,只是voronoi距离场),基本原理在当前像素周围搜寻种子并把最佳距离的种子保存用于循环这个步骤,最终得到种子蔓延的距离场数据,而这个算法的关键点在于这个像素的周围这个距离并不是一个固定值,而是2次幂的距离衰减最后到一个像素的距离,这种搜寻的特点被形象的称为jumpflooding,速度和种子数量相关,收敛速度为指数级别。 对于这个demo,更重要的是展示了一个利用jfa产生的voronoi距离场渲染三角形的过程,层次lod来自于buffa的灰度平均图的生成,其中有一个阈值决定种子密度,bufferb利用buffa的灰度种子做jfa产生第一个距离场,bufferC利用bufferB的voronoi图查找出三角形,并再一次用三角形做种子计算jfa得到正确的三角形网格,bufferD,Image优化并渲染。

2月14日

这是一个渲染技巧和优化算法等细节拉满的优秀demo,农场的cell区域的计算使用了repetited cell,一种不用voronoi,无hash的快速算法,spine树木使用cubicbump构造的形状进行z轴旋转塑性,其中sdf使用iq的隐函数梯度法改善sdf畸变。树体使用两个smooth blended的球体和packed sphere repetition进行塑性,packed spthere repetition原理就是在每一层上错位让球体紧凑排列。此作中值得注意的优化技巧,使用icdf计算的e指数分布的noise,用于计算远处云体的density。 march部分使用了一个pass循环,阴影,ao等处在不同的pass。image部分根据iframe渲染四帧画面到subpixel的四个象限(画 1/4屏幕),减少像素计算和采样量。

helix又叫coil的sdf,不同于普通的通过在笛卡尔坐标系下做atan和rot变换空间的helix,此demo演示了一个在polor空间做旋转变换,repetition,计算closet point的方法,以及一个笛卡尔坐标系中计算的helix坐标系。在polor空间中只需要一个简单的投影就可以计算helix 的closet point,值得注意的是代码中取atan(line)的角度再把point旋转到这个角度再做repetition,这段的本质其实也是把point投影到line上.在不同空间中对角度和长度的直观理解完全不同,譬如角度和投影,如果从向量component的比例保持一致上去理解就想通了。

这个demo演示了一种相比于voxel 更简单的sparse grid marching的方法,相当于利用floor和fract把空间放大,粒度变粗,在基于floor的大粒度中march大形体,在基于fract的小粒度之中march小形体。多lod层级的化就只能voxel dda了

Truchet tiling的范例,truchet的图案的每一个格子(tile)内是通过对角对称绘制的两个半圆弧,每一个格子的对角方向都是随机但最后会产生连续曲线效果。此例中值得关注的点是通过每一格子取反获得三角箭头的流动,对圆弧atan转换到polar空间repetion绘制箭头和直线。最后背景和轻微鱼眼的效果值得参考

2月15日

一种指令更少的voronoi图案,没有hash。原理是打格子画圆,然后做一个空间偏移再画圆,关键点是用min获得这些圆的边界。这个技巧非常棒。

用于改善iq的soft shadow 在某些情况下出现artifact的情况的新的soft shadow算法,重点在暗黄色点的计算保证软阴影的连续性。这是作者在他的claybook游戏中使用的方案。

大象的这个例子使用了分层绘制,背景层画树叶,灌木,土地,天空,太阳,简化了的大象小象以及ao和软阴影,并且做了reproject taa,后面bufferB又做了dof。前景层则是大象,影子,ao等的绘制,大象的鼻子用了bezier,牙齿和眼皮位置通过空间弯曲塑性,耳朵用了几个圆做了剪切,边缘根据atan做了sin扰动,第一层的叶子用了个smax+abs做双向压扁。 另外大象纹理用了iq发明的二向映射biplanar(比tripalanar少一个采样),地形用了conetrace计算ao,背景层的简化大象软阴影用了解析椭圆软影(iq发明),同时整个例子充满了iq式的塑性技巧,值得学习

一个实时模拟dispersion色散的例子,整个光追GetRenderSample()流程:计算视线和cube的交点获得反射和折射两根新射线,折线计算由传入的波长计算出ior通过refract获得,反射线计算环境反射,折线计算反射强度和环境反射相乘,然后进入cube内部循环多次进行反射折射的计算并累加, 其中环境反射变成了用折线计算,法线取反,两个ior媒介对调,计算出的反射强度的倒数(1-反射强度)乘上环境反射和距离衰减因子(absorbsion)。最后再对GetRenderSample进行循环多次填入不同的波长进行累加得到最终颜色,最后伽马矫正,CHROMATIC_ABBERATION。本例中GetDispersedColor通过传入波长获得rgb颜色的模型用了一个sin函数,简化了光谱计算。

2月16日

模仿《journey》画面的作品,亮点是卡通云的塑性,沙滩的渲染,以及飘动的绸带,卡通云是由横向的repetition的圆在水平线的扭曲空间下smin融合后塑性(没有使用hash标识每一个圆做随机),云在z轴方向上也是做了空间缩放,并且争对法线做了调整,使得高光区域呈现这种卡通风格,沙滩的塑性使用了smooth后的trianglerWave三角波噪声 gliching的亮点在沙滩的rim区域(接近切面视角)进行加重,是使用了采样招噪声图调制spec高光。飘动的绸带使用triangleWave和一个blend函数根据时间和高度位置调制而成。此作的另一个特点是把所有的sdf位置和影响风格的颜色都当作全局const提取出来方便调试,作者做了三套主题分格值得一观。

速度极快的advec流体效果,此作粒子的位置就是buffer的位置,因此没有必要使用voronoi tracking,但是会有求临近的插入排序,作者设计了一个swap函数来做插入排序,值得参考。

胖胖的立方体是用length16塑形,gliching分为内外两层,一种简单的非pbr的Anisotropic高光(各向异性)模拟, 原理是用噪声调制成发光方向和半法线H(normalize(L+N))点积作为anisotropic指数因子传入spec高光的pow函数,第一层的aniso方向减去了normal方向后再和半法线点积获得aniso指数因子,这样就去除了法线方向的gliching,和物体高光错开。

2013年的作品,作者把voronoi打散一行一行插入排序获得领边节点信息,提升性能?胖方形也是length16塑性,anisotropic模拟使用了两个最近voronoi邻居作为权重插值计算得到aniso高光(pow)指数因子,算法流程和 20200723~204416相同。本作加入了lensflare,用一张纹理图作面向光源时的脏镜头。喜欢这种金属的抛光效果的模拟。

iq的经典跳动小球,此作的亮点是动态模糊和无状态(stateless)小球运动,实现部分也非常精彩,由于使用了很多解析方案(小球的模糊)使得帧率能够保证。首先小球是传统的根据地板打格子作repetition,hash标识每一个球体并赋予时间,偏移,颜色的随机属性,每一个球体的运动有自己的时间t,用t拟合出一个自由落体的曲线,对t求导获得速度。由于是stateless,这里的速度只是用来绘制球体的模糊。 球体的模糊用了解析解的iSphere碰撞检测,通过传入位置和速度,能返回运动模糊信息(alpha值)。其中的速度并不只是球体的速度还加入了相机运动,视角运动,地板运动速度加在一起用来计算运动模糊,运动模糊也参与到了球体影子的渲染。另外值得一提的是raymarch步进使用地板格子作dda步进,提升marching性能,另外在步进中用trace出的alpha值进行blend,最后再和background进行blend.

展示了空间folding的demo,核心在fold这个函数,输入offset和rot对空间先进行偏移和旋转后然后统一绕z作a角度的旋转然后再回到原来的坐标系,这样使得所有方向的空间弯折都在一个轴上进行,作者用时间对折叠角度进行调制产生折叠动画,本作的唯一缺点是纸的两个角的折叠,折叠完了由于没有保持坐标系,所以这个时间点后需要两个剪切操作把角隐藏,否则折叠结束两个角会回到初始没有折叠的状态。 纸张用来一个sdbox,简单的diffuse光照处理。

本作是claybook作者演示3d voronoi 镂空的demo,但其实亮点是他的conetracing和prepass加速光追部分,先用conetracing的capsphere半径(cone beam随着步进半径变大,到达碰撞点目标时的半径)初筛得到一个新的ray起点,写入prepass,image pass再在这个新起点进行raymarch和着色。3d voronoi就是2d的升级版,右边的voronoi同时计算了最近两个节点形成的边,并计算了垂直距离得到的有边界信息的sdf再和球体作剪切(max),实现voronoi镂空效果。

14年的demo,展示了在一个pass内通过cone tracing和apertured,focus等参数,模拟出dof或者边缘模糊的效果(反走样)。具体原理是用碰撞点和碰撞时的cone半径调制出一个coverage,作为权重累加trace到的color,获得边缘模糊和focallength的焦距效果,整个过程只是在当前射线中进行,没有多条射线(cone angles分布)参与dof。

蛇demo属于巧夺天工了,原以为头部的纹理会有很多hack代码,其实只是一个上下对称,用sin进行辐射扰动的voronoi。而蛇身的scales用了两排半圆叠加并提供了鳞片tiling的一些塑形参数,并提供了uv,这两个scales tiling做的异常出色,值得借鉴。嘴部就是根据z作了个smoothstep曲线,并对曲线截面进行微调,得到一个小突起加到头部的sdf中,没有用smax。舌部的伸出动画用了smoothstep调制的sin,但是舌部上下的弯曲没有用sin,只是用abs fract根据时间t算了一个zigzag 用这个zigzag和z作为角度在yz平面绕x旋转,最终获得一个非常不错的bend效果。在塑型和动画的coding中不一定非要用到smin,smax,sin/cos等标准三角函数。有时候根据xyz位置配合abs fract,smoothstep都可以拟合出更合适的曲线。

2月17日

经典的多重重要性采样pathtracing的教案,不过由于场景简单,光追迭代的深度只有一次,并且省略了diffuse 的brdf, 只考虑spec高光的brdf。这边的多重重要性采样,指的是两条线路采样,一条场景线,一条灯光线,计算他们的brdf反射率,反射方向,通过他们的 pdf对颜色作加权累加, 最终的颜色值累加到当前的bufferB再Image中除以frame count得到最终的光照积分收敛值。

这是经典的海洋和云的融合。重点在海洋和云的塑形以及渲染。海洋用abs(sin(uv+noise(uv)))和他的垂直方向用他自生进行插值获得推浪的塑型,再用不同的amplify和chopyy以及frequency叠加octave。云的塑形先用两层贴图噪声和pow调制weather相当于云体的shape和多寡,再用累加最多两层fbm产生云细节,最终调制出云density.云体塑型使用到了地球单位的数值。 渲染部分,云体的渲染就是在当前碰撞点一层一层延太阳方向步进叠加云体的density,再根据太阳方向计算散射量,再用相位函数phaseFunction计算bearsLaw衰减,获得lightRay的radiance, 这里的相位函数用了mie的数值拟合曲线,最后skyRay进行raymarch,对每一层lightRay的radiance进行距离衰减等光照计算再积分,最后和背景色插值,获得最终颜色。水体的渲染用二分法进行raymarch,主要计算了schlick的fresnel得到高光,HG作为相位函数计算散射,最后叠加根据高度调制的泡沫以及太阳光投影在xz平面计算的foam shadow。 最后是方块用了schlick的高光计算,叠加软阴影,海面的方块倒影和云体影子根据和海面的碰撞点位置的反射方向最后计算并叠加。本demo使用了YCoCg颜色空间作剪裁的的taa,然后blur效果,ace tonemapping.

演示了Ashikhmin,Charlies两种分布函数和遮蔽因子V(G)的拟合函数在pbr中的效果。spec = lightcolor*fdg*PI*NL,diff=简单的nl*lightcolor*diffusecolor.

本例展示了voronoi噪声在冰面裂缝上的巧妙运用。首先是voronoi噪声获得border,再用三角波扰动,用不同的参数制作三个crack sdf(xz平面),再分别trace他们获得碰撞点cp,并计算normal,冰面裂缝的渲染需要利用cp获得水下深度,并用normal作调制(模拟裂缝的角度对光照产生影响),通过深度计算和深水颜色的插值获得光线衰减变化的颜色。三层裂缝做一下插值获得最终裂缝颜色。然后利用fresnel根据视角e在天空反射和裂缝颜色之间作插值得到最终冰面颜色。远山和天空一起简单noise运算,值得注意的是远山和云fbm用了atan(e.xz)产生响应视角变换的环境贴图的效果。 冰面上的雪花,雪雾也是几个noise插值他们的颜色,小水泡也是noise,利用intersectplane对水下作三层碰撞,用当前层的xz平面获得noise,然后pow后获得气泡颜色。值得注意的是本例中的trace用intersectplan代替获得来获得视角方向的追踪点p,snowwind雾的噪声扰动空间后再fbm效果不错。

本作展示了一个基本的无状态stateless抛物运动,核心是tracespherepath函数,主要循环五次计算五根抛物线(五个圆盘),每根抛物线会利用时间切片获得当前的位置信息,循环五次是因为中间要计算速度(在每一个圆盘起点速度都会从0开始),整个抛物线只需要2d的公式就可以了。最后N个小球就循环N次追踪下(本作用的raytracing,没有用raymarch)。值得注意的是本作的softshadow是通过碰撞检测圆盘获得靠近距离,用这个距离在0-1之间插值获得,是一个避免了tracing的解析方案,

本作的特点是快速的实时冰材质模拟,没有用到fresnel(pow指令),而是根据视角和法线的点积计算一个alpha值决定后面是作折射还是反射计算,小于一定角度的total internal reflect全反射直接按法线反射到外面去。整个sdf的raymarch包含内外部两种参数,内部是负数所以dist取反即可。冰的颜色设定三种背景色作为crefract,crefract根据rayLen厚度和自身的背景色插值。 环境色作为creflect,内部iceinner颜色用来白和灰中间的插值(常量)。然后之前的alpha值相当于fresnal在反射和折射crefract颜色之间插值获得冰块的颜色。 renderIce一次性计算反射和折射的方向,法线,raylen经过的路径的长度(厚度),以及类似fresnel的alpha。本作值得一提的是softshadow用了光线方向的conetracing计算dof的方式来计算。

本作展示了实时玻璃折射+色散在分型上的效果。在分形上作玻璃折射的困难点在于获取内部的折射碰撞,而且相当费。作者其实直接用了计算获得的refract射线采样背景色代替了,而关键部分是这个refract射线rgb分别用了3种ior计算折线采了背景后获取对应的通道refractcolor.r = backgroung(refract(dir,normal,0.9/ior1)).r,refractcolor.g = backgroung(refract(dir,normal,1.0/ior2)).g,b以此类推。 reflect直接是背景色采样。最后通过计算fresnal然后在refrect和refract之间插值获得最终颜色,fresnel的计算用到了折射公式eta。本作中的ghostflare,holoflare都非常值得借鉴。另外还有bloom和dof效果。

一个展示视锥内lod细分的的例子(clipmap) ,首先是一个lod函数通过视空间的距离log2计算获得,接下来就是循环lodM次对格子内的空间作2*xy-child的细分迭代,画边界,累加颜色区分上下lod,过程中触发lod满足条件则退出迭代。其中有一行代码child=min(U,1.-U),相当于mod的作用,这种用法值得借鉴。

这是一个模拟nomansky星际穿梭的效果,原理是沿着中心辐射的方向共循环10次绘制10圈线条,颜色累加。重点是这个线条通过pow(1.0-8*length(fract(屏幕四周辐射的normal)-0.5),2.)进行调制,产生边缘柔化的效果,这种在normal空间sdf辐射线条的例子(不用mod(atan))一下子脑子转不过弯来。第二大亮点是通过离中心点的距离来调制线条颜色,让红蓝的移动效果根据速度产生变化。速度是和当前离屏幕中心点的距离正相关的。

iq这个作品主要演示了iq的simpleIK算法,虫子的运动和塑形写的很优雅,典范之作。首先是simpleIK只有一个关节的情况,可以把问题简化成已知两个圆心O(身体),P(地面),半径分别是r1,r2, 构成两个圆,关节位置X其实是这两个圆相交的交点,iq给出了求解过程。然后虫子塑形部分,先变换到p-monster.center,投影到monster.mww(forward),mvv,muu坐标系。身体是个椭圆直接在p位置(已经变换到monster.center)叠加个bump,腿只画了3个,另外3个x轴对称,通过计算i标识从monster.ne[i]获取对应的关节位置,腿分两段小腿数据从monster.fobstacal[i]获取 然后用sdSegment叠加sin+bump调制画出,左右对称只是取了一个p.x的sign计算对应的位置即可没有用abs(p.x)。最后就是通过前后时间获取xz上的位置以及地形碰撞的位置,计算出monster.mww虫子朝向,solve六次计算出monster.ne[]。虫子的身体材质是在monster的坐标系下计算。接着就是diff,refl,spec,occ,softshadow等渲染,值得一提的是demo中rotationAlign计算旋转矩阵没有用到acos,iq提供了优化算法.

这个例子和20230201~202736的框架基本相同,同一个作者。粒子部分的核心函数在BufferA中,particleStep负责物理, neighborUpdate负责更新邻近粒子(整个屏幕范围随机采样和自己的当前邻居对比的机制,优先原来的邻居和邻居的邻居先更新一遍),BufferB 则是Voronoi particle tracking buffer的更新,原理和粒子的邻居更新机制类似,用当前像素获得当前的最近邻居id,和当前像素的视线碰撞,对这些邻居进行排序更新,其中优先对全屏幕随机捡取的id,当前像素一个范围内的随机id作比对。 这种搜索过程称为voronoi tracking,优点是不用字典,缺点是粒子数量多时无法在几个frame内找到正确的最近邻居(盲目随机,增加象限过滤效果有限。),本作的框架不同于20230201~202736的框架,voronoi的tracking没有用投影到屏幕空间后的屏幕位置(叠加z因子)作tracking排序。因此性能一般。 另有不同之处是这个作品用的屏幕空间ssao,之前的版本用的屏幕空间dda获得更精确的反射碰撞,渲染通过压缩的Gbuffer信息完成。

反常色散的例子,指的是折射率与波长关系在吸收区左右不同于正常色散,使得他们的曲线呈现不连续的情况。本案例的光追框架设计的小巧灵活性能一般,就用来一个bounce函数和一个Bounce结构数组,bounce的数目由设定的可见波长数。对每一个波长的bounce迭代一定弹射次数后,根据每一个bounce的波长计算权重加权累加这些bounce的reflectance,其中权重对rgb三个通道是不同的。这个过程就是根据一条转换波长到可见RGB的曲线计算最终颜色的过程。bounce函数内部包括了关键角,全内反射,射线弹射方向,fresnel,reflectance等的计算,sdf通过sign判断内外。 本例中关键的反常色散的部分在 iorCurve这个函数,是一个波长到ior(折射率)的转换的拟合曲线。钻石的塑形就是floor(atan)后生成一系列相等分布的平面作max平面切割,不过由于用到了大量sincos性能一般.

这是一个简化+优化版本的钻石渲染。sdf塑型先把空间旋转到floor(atan)的角度,投影到预定的一些切割平面获得sdf数据再旋转回来,最后对这些平面sdf作max切割。这样作的好处是把切割平面固定下来,旋转钻石空间去切割,避免了sincos指令来确定切割平面。这种塑型方式非常值得借鉴。本作的渲染简化了很多,3个ior填入vec3和一个通道权重点积获得来获得ior计算refract。用于计算reflectance的fresnel用了pow代替sincos方式。最后rgb通道分别trace完成渲染。

一款p=p/(p*p)的分形图案,亮点在于在1/d这样的倒数空间进行raymarch,和log空间raymarch异曲同工,d要加上一个小偏移,避免无穷大。测试log空间同理,需要给d加上一个偏移。原理是分数维分布空间如果用线性空间去raymarch,步进没法收敛到正确的位置.

一款注解详细的基于voronoi tracking的粒子效果。buffA是粒子物理模拟,buffB是voronoi tracking buffer,BuffA通过采BuffB当前像素周围获取邻居,再用邻居id回采Buffa获得物理信息。BufferC通过获取当前像素四周ID再查询BufferA得到位置,然后利用exp对四个邻居中范围内的进行绘制,颜色使用了hue转换,space按键切换成voronoi tracking buffer的绘制模式。 代码中使用了一些宏方便书写,整个结构简单,值得借鉴。

2月18日

在y=0的位置画五根弦,uv.xy随着uv.x对uv.y作sin扰动,由于每根弦赋予一个相位和时间一起填入sin,实现了五弦的连续起伏扰动效果。用音乐频率作因子对每根弦赋予弦的宽度。由于采用y=0上绘制,只需要一个abs(lineIntensity/(p.y+A)即可得到高亮突出的激光效果,A是声波,因此激光的线宽会随着声音变化。横向上用了abs(mod)参与调制lineIntensity由此对每条弦做了个横向光波的效果。

iq的音频数据格式解析demo,横轴转换成88个钢琴键key,每个key再转换成A~G的Note, KeyToFrequency是key到频率的转换,Amplitude得到的是对应频率的音量大小。跳动的长格子用了一个0.01/(p.y-amplitude)来绘制产生边缘fading的效果。

这个效果相当出名,但是实现上太多循环,很费。塑型部分就是随着x轴对yz空间旋转,然后sdbox再叠加noise,用时间调制完成。费的部分是除了softshadow,normalmap,他还计算refract,得到refract在内部再积分一个noise的nebula星云的效果。内部和外部分为两个颜色color和color2,最后按一定比例混合,Image部分加入音乐,并使用了一个simplyfied version of Dave Hoskins blur再循环80下按某种规律采样周围像素,然后按权重叠加出最终颜色。

一个随音乐跳动的方块地板的demo,本作的亮点是直接用4个音乐频率当amplify作octave叠加来调制每一个格子的高度mapH(vec2 pos),返回高度和格子id。另trace是在2d grid上作dda,获得和每一个格子的碰撞点再继续作一个dbox的raymarch获得这些格子的sdf,值得注意的是本作并没有使用repetition的方法去实现trace, 光照部分使用了两个cone light,一个作为主光源,一个是back light,另外还有softshadow, occ等,occ=mix(pos.y,1,normal.y)(获得侧面和顶部的遮蔽)这种取巧实现ao的思维方式值得学习。

本作的效果非常适合音乐VJ,大致算法是前后12层mod(atan)的rect作叠加而成,值得注意的亮点包括一个smoothstep_noise,一个synthcol这两函数。synthcol让一个颜色旋转一个角度再和构成的平面法线(颜色)之间按scale作插值,角度由输入的phase控制,这样只要变换scale和phase就能形成漂亮流动的调色板。

本作也是非常出名的一个音乐视效,首先是getPoint函数根据传入的偏移和id(floor位置)获得节点,然后类似voronoi,获取当前像素位置四周九个点,对他们进行连线。连线的距离来调制颜色visible,会对每一个节点作一下按sin跳动的sparkle,整个绘制在layer(p)中完成。最后mainImage循环4层layer,按照fract控制他们的密度和明暗。整体颜色变化用sin调制

atan变换到polor空间,fire函数是几个noise的叠加,再用fire对fire的uv进行扰动,高亮用了pow收缩光束,ramp函数提供了一个白粉蓝渐变调色板。整个效果由于在圆周上部顶端处存在接缝(fire迭代扭曲的空间造成不连续),因此把上半部分颜色压暗上下镜像叠加处理了。效果可以算法不优美。

我认为这个效果最炫酷的地方就是中间的火球光芒和星际的融合。星际是几层backstar按不同的offset和mult叠加,backstar部分就是fract,floor获得uv的格子id用hash获得star位置(噪声的基本原理)并计算sdf,穿梭光晕部分很炫酷,大概就是把uv变换到角度和长度的polor空间rss,再作一个小旋转产生扭曲,再用一个abs(fract)变换到repetition空间,最后作0.06/length(rss)的倒除压缩范围。整个过程中再用soundwave和lum(noise)作调制。然后是godray部分,一个循环沿着辐射方向采样basecolor累加获得shine。最后color=max(shine,color)。分型部分暂时没看。

极少的代码完成酷炫的效果,R函数是变换viewdir,H是对路径扰动,p是trace在3d空间的点,p直接通过归一化uv后的d(viewdir)乘上步进获得。p=abs(fract(p)-.5)变换到对称空间, p=p.zyx:p 这种代码是用于计算格子的边界的空间,e是计算点sdf得到emision. 本例对raymarch边界以及路径旋转的简化操作值得借鉴。

这个demo在Spherical球面空间和hsv颜色空间运算,road是笛卡尔空间。本作的亮点是流星,天际的glow和太阳。流星用了一个length(p)点状sdf的pow衰减 乘上segment(p)线状sdf的pow衰减,根据时间片调制而成。天际的glow根据rd视角和太阳的法线点积乘上abs(rd.y)当作glare因子.完整的调制公式是glare=pow(abs(dot(rd,sun)),20); 1.0/abs(rd.y)*mix(0.005,2.0,glare); 。本作的aa是偏移rd获得两个trace点根据他们的length以及地平面的倾斜度和x轴位置调制出aa. synth函数是把横向轴拉长用abs和mod画出的音乐跳动的方格。

激光线是1.0/abs(p.y)*BRIGHTNESS*getFreq, 最后color = color+lum(color)增强中心亮度。

.2/abs(length(p)-i)画激光圆,再用atan(p)*sin调制成圆弧,同时用atan和i调制颜色。 i是循环的层数。喜欢这类效果的原因是不用bloom后期,通过1/x倒数或者pow又或者通过dot(view,sun)获得glare和激光线的技巧非常棒。

这个作品同样是trace几个plane,然后alphablend。每个plane包含一个effect函数核心部分。effect先把p转换成hextile空间获得每个hex格子,同时也把原来的p转换成polar空间再再这个空间用toRect变换到rect空间,这样就可以调制颜色了。glow部分是BufferB用了bloom实现。值得关注的是hextile空间,polar空间toRect空间的变换.toRect(p.x*cos(p.y),p.x*sin(p.y));,本作同样使用了通过偏移viewdir多计算一个新的trace点,利用他们的len调制出aa.

本作的也是不用bloom实现光晕,同样使用了一个starField(uv)的函数利用fract,floor完成一层的星星位置计算,star函数通过smoothstep(s,0,sqrt(length(uv))对星星进行绘制,s是vec2的一个色阶权重,最后星星是color*(s.x+s.y*0.1)这样就有明显的边缘色了。这个技巧非常棒。另星星背后的烟雾,是采的一张鹅软石tiling图,每一层的烟雾位置都要用上一次烟雾值进行一个扰动,最后叠加每一层。实现细节丰富的烟雾扰动。太阳位置的光线是atan的polar空间采样噪声贴图进行绘制的。 整体的太阳位置是一个common里CalculateUv的函数,中心汇聚运动是通过fract(time+i/itter)*uv获得。这种按时间循环层叠空间的方法值得借鉴,各种loop视效中比较常见。最后buffA有一个累加自身*0.2的操作实现星星拖尾

本作流体的框架也是比较常见,BuffA用于计算粒子位置的物理模拟,同时通过check函数进行voronoi particle tracking(check就是个list sort,推荐这种把偏移作为参数的写法方便添加偏移方向),其中如果当前density大于一定阈值,则把当前位置保存下来的操作相当于产生了一个新粒子(最近位置被绘制和计算)。BCD buffer都是运算divergence和desity,多个buffer加速运算,Image负责对靠经p当前位置的粒子根据density进行绘制。本作不用random去暴力排序替换粒子是因为这种流体运动的粒子之间的相关性,附近的粒子基本都在附近。

这个流体demo的特色是ABCD的代码一摸一样,实现邻近查找,并计算粒子的物理,采用A采D,B采A,C采B,D采A这样的循环方式加速物理, Image部分采邻居,计算颜色,绘制。

这个作品就是好看,渲染上把海面叠了exp距离雾,这个作品的重点是几个海面wave的模拟,通过旋转进行octave叠加,太阳就是dot(viewdir,sun)

本作的意境和配色非常搭,有几个突出的亮点,第一是山脉分层的raymarch算法,原理是按间隔步进,获得的terrain和p.y的差值d用一个smoothstep和AA因子计算出当前片轮廓的alpha,用这个alpha每一层作alphablend。第二个亮点是AA反走样的处理,获得一个视平面微小偏移的射线,和原来的射线在每一个步进间隔的碰撞片上的差值作为AA的blur因子和之前的d一起参与计算轮廓alpha,本质上就是让两个相邻的像素产生了相关性,同时也有dof的模糊效果。 加入高度雾,距离雾,本作中的noise也使用了value noise和fbm terrain。value noise在iq的代码中非常常见,因为iq给出了value noise和fbm的derivative解析解,因此iq的作品帧率很高,比普通的中央偏移法快6倍(不用raymarch 6次作差值计算法线了).另外本作的alphablend有一个特殊版本,值得注意一下。星空用了mod+octave,特点是给每一层星星一个sin变化,并且相位不同,总的来说标准的做法后面还有很多,这里不多表。

这个射线其实是用sin(atan(p))的圆周周期值,然后pow后得到高亮区域的激光效果,可以控制数目。把这些射线再乘上不同运动的fbm噪声,得到不规律的雾中射线效果。底层噪声用的value noise

本作的亮点是星星的渲染,用了一个随机噪声作为pow的底,指数用了一个类似box的sdf函数taperbox作为塑形。也就是说在塑型内的区域才会让星星发亮,从而绘制出类似银河的效果。taperbox同时也用于树木的绘制。本作的山脉间隔绘制和20230203~002403类似,不同的是没有射线和视角,传入平面坐标,循环alphablend 每一层layer,纯二维的处理。

用了一个simplex noise作为rotate旋转空间的角度,调制线条的扰动,n个线条就循环n次对color累加

横向拉大30倍,然后fract,然后用smoothstep(a,a+1,abs(fract.x))*smoothstep(a,a+1,abs(fract.y))调制出一个被拉长的圆形的sdf,填入sin的size. 代码里有hue的计算公式。

这个线条的影子是通过画两条线,第一条是带颜色的,第二条宽一些的是黑色影子,然后利用第一条的sdf值在第二条阴影sdf值和颜色之间插值,这样就绘制了带有影子的线条。线条本身通过循环绘制n次竖线,在x轴上叠加一个simplex_noise(y)的角度,整体上再根据不同线条i作一个旋转。

这又是一个层叠的列子,trace每一个plane,一个步进一个plane,作alphablend, alpha因子加入aa由双射线trace点的length调制。这次plane的内容是万花筒里的chucet图案。万花筒需要变换到polor空间作mod 的空间repetition,然后再toRect变换回笛卡尔坐标系用于绘制chuchet形成绕圆周repition的chuchet。这一部分值得多思考一下。

循环绘制n条用sin plasma调制的曲线,曲线绘制用了smoothstep(lightwidth,0,abs(p-t)); t为y轴位置。这个2d plasma可以复用。

这个模拟流体的水墨效果本质上是一个domain扭曲,用newuv = texture(uv) 进行循环,不过这里newuv会转换成一个随时间旋转的向量叠加到当前uv,同时在当前uv再叠加一个根据周边像素计算的-normal,作用是增加扩散。而texture其实在外层叠加了一个fbm产生分形的细节。最后fragcolor=mix(old,fragcolor,fade)作淡化. Image部分计算normal,spec,作高光效果。其中rainbow的tint计算值得参考,其中还根据buffA的值和p.y调制ranbow的强度的相位.

这个例子应该是展示了2d层叠的影子和纸张的渲染。纸张paper函数是用x,y两个方向和0.5倍率分别采一张噪声作叠加,然后是通过周边像素paper(xy+delta)-paper(xy)..计算bump用于光照,bump叠加了一个旋转设定一个合适的光照角度,影子和圆切效果就是个sdsphere+noise, 影子就是做个偏移再计算一个sdshpere获得shadow的值用来在颜色和影子颜色之间作插值。不同的层就是循环计算paper后叠加。没有用到alphablend.

这是一个很简单的音乐视效,圆是1./abs(length-rad),color是hsv空间用atan调制,线是1./abs(y) ,用音乐频率调制颜色,线在x轴画然后旋转空间。 整个激光线的color叠加一个luma(color)提亮中心。采样音频用了getfrequency_smooth, mix(tex(x),tex(x+1),smoothstep(0,1,frac(x))得到两采样点之间的smooth插值。

这个作品有很多值得参考的亮点,首先是随机外星文字算法rune, 循环四次绘制线条,每次采样随机贴图获得fract后的posA,posB,对posA根据当前线条i作贴边处理,然后对posA,posB在一个2x3的grid里作用floor实现snap,posA,posB太近则continue,min四个sdsegment得到文字sdf. 然后是火焰部分color(p)非常赞,vec2的p加上iTime的z,用abs(3dvaluenoise(vec3 p))得到一系列n,做octave叠加,得到文字的sdf,和n进行调制,然后让pow(color(1.0,0.25,0.08)/n,2.)获得火焰扰动效果。可以理解成把一个noise+sdf调制成的值压缩在火焰颜色里,类似1d的1/length(abs(x))线条的绘制。 最后是这个作品用的是3d raytrace框架,用了一个tween调制出sdbox所在空间的定时旋转,然后在sdbox处计算出uv再去画color()的文字和效果

这个例子BCDBuffer都是box fliter blur,没有用到dof,由于不同线宽的原因产生了dof的错觉。比较多的的circle,sector,dot的sdf函数值得参考。最后image用了一个smoothstep(-0.2,0.7,col)把颜色压在这个范围内,和vec3/col 异曲同工。

核心函数是noise和rayCast, nosie用来两次迭代扰动采样鹅卵石图并且用time调制产生运动的xy平面团状的noise,rayCast循环70次旋转按p.z对p.xy空间进行扭转,采样noise计算density,对density做pow压缩,然后叠加density再除70。纵向颜色用i/iterations lerp两个color。skybox用rayDir.y和length(rayDir.xy)对三个skycolor插值获得,最后sky+effect得到最终颜色

这是一个高性能海洋的例子,他的高帧率来自normal的计算,使用的是解析解,对每一个octave公式求导累加。而非map(x)-map(x+delta)这种采样周边像素计算梯度需要xyz轴向6个map。太阳的绘制用了smoothstep(length(rd.xy)-delta,length(rd.xy)+delta,sunrad); sunrad是固定的。这种算法比dot(rd,sundir)有意思,他的太阳位置是通过rd*rot获得。 云的绘制也很有意思,没有使用噪声和density之类,而是循环s,对rd旋转s,累加s,在用abs(dot(sin(p*s+iTime)/s,vec3(1.65)))调制出一个e,在用e=smoothstep(0.5,0.4,e-0.095)压缩出边界。注意s是从大到小的循环数值,如果反过来就画不出边界,值得研究研究这种噪声。最后海面的渲染有用到refract(rd,n,1./1.33),用一个pow(dot(refract,sunDir),35)模拟次表面散射,最后用双循环偏移xy做了个AA

这是一个特别的隧道视效,每一个隧道的塑型是xy平面的slice的distance,distance用sincos调制再radius/length()获得,然后按照随机把这些distance累加获得n多个隧道,其中一个隧道是相机的path, ,trace也比较特殊,先hit到一个点,再用二分trace得到精确的边界, normalmap用了triplanar采样三张纹理图获得再和几何法线n调制参与diff光照。纹理部分由点状和stripe状混合而成,没有使用纹理贴图。点状是用了pow(sin(p.x)*sin(p.y)*sin(p.z),3)调制,带状用min(atan(sin(p.x)),sin(p.z))调制。这种纹理算法可以参考

本作的亮点是de这个塑型函数,p*=1.2-len(sin(p))*.25是把空间转换成胖方形,然后再xz平面旋转y角度,yz平面旋转x角度用时间调制角度,形成空间扭曲拉伸的效果。后面部分就是一个球面上点状的sdf用了len(sin(p)),最后再和中心的球体sdf做min合并.背景线条部分用了smoothstep(.15,.0,abs(fract(p.y))),给人一种远处模糊背景的感觉, trace用了一个循环步进计算了反射,最后和简单的diff着色混合。

这个demo演示的是一个叫做hashednoise的函数,实现了不用后处理完成噪点风格的画面,hashednoise函数填入p,dpdx,dpdy,获得按透视距离分布的固定的noise,远处就不会出现高频噪点。地形是几个smoothnoise(lod)采3d噪声的octave叠加,但是对p.y高度下面的octave会返回掉,相当于一种cascading return,用于优化.星星部分很有趣,star(dir)函数用pow(atan(dir.x,dir.y),20)获得trail,用step(abs(dir.z),0.007)获得拖尾宽度。整个过程只是使用dir。非常值得学习。最后step(hashednoise(p,dpdxy),color)得到噪点风格.

本作和20200720~130109的框架一样,着色处理不同。BuffA用于计算位置信息并使用check函数做插入排序实现邻近查找。也就是voronoi particle tracking. 然后BufferB,C,D循环加速divergency和物理属性的计算。Image采BufferB的vorticity进行绘制,前文提到的那个demo采的是BufferA的density进行绘制。

这个作品用了4根ribbon,每根ribbon使用一个sincos曲线调制sdbox获得方形的曲线。sdbox(p,vec3(cos(p.z),sin(p.z),...),size); 更神奇的是线条区域光arealight的模拟是这样的,color+=lightclr[i]/ribbon[i](p-n*.01),其中n是包括ribion在内的场景的法线,其中乘上音乐的不同频率调制每根的亮度。Image部分用了fxaa,FxaaPixelShader 这个函数值得借鉴。

这个粒子效果让我回忆起了80年代winamp音乐播放器里的一个视效,这个demo初看比较绕,BufferA计算的是粒子的位置和方向d以及速度v,而他是根据bufferC的绘制层Gaussian blobs渲染出粒子形状和拖尾后得到的数据(写的其实也是d和v,同时又充当颜色),BuffA的粒子使用一个左中右三个方向和搜索半径在bufferC中采样累积用于决定转向的阈值,然后把BufferA的粒子按屏幕x三等分,一部分根据方向阈值更新自己的方向,一部分根据速度阈值更新方向,一部分根据密度。 BuffA的粒子位置和屏幕坐标无关,在鼠标点击或者开始帧的时候把按照屏幕五等分的粒子设定在初始位置(但方向随机).BufferB负责voronoi tracking,把附近的粒子ID和屏幕位置联系起来。BufferC用了一个GaussianBlobs的算法对粒子进行绘制同时产生拖尾,Image copy了BufferC. 这个令人神奇的菌丝效果的计算(粒子位置的计算原理)并没有过多注释,不过根据整个流程基本猜出个大概,需要多玩这个效果,非常不错。

这是一个肥皂泡泡薄膜干涉的例子,光学上就是光线在薄膜的两层之间反射,由于两层薄膜非常贴近造成两条反射光线形成干涉从而出现色彩。本案例中有较多可复用的光学代码,根据ni,nt两个媒介的ior计算的Fresnel,FresnelR0,GetCosT(ni,nt,cosi),XYZtosRGB,WavelengthToXYZ(把波长转换成XYZ颜色空间),ThinFilmAmplitude函数根据波长和厚度以及cosi入射角计算干涉因子,GetThinFilmColour通过入射角cosi和厚度参数把可见波长转换成XYZ,同时计算干涉因子amplitude,对XYZ颜色加权,最后转换到RGB取平均. 本例中还有一个ArtofTheLastofUsPartII在SIG2020提供的在lastofus游戏中使用薄膜算法。代码中给出了youtube链接。BUFFA把薄膜颜色做成了表用于优化性能。Image中Trace泡泡,渲染泡泡,这两个也值得提一下,搜先泡泡本身用了sincos对球体扰动,位置部分是把x轴变换成前紧后松的非线性空间bubbleForward函数,再做floor离散化获得泡泡的位置,然后一个for循环获得当前泡泡位置加上前后泡泡的位置后使用inverseBubbleForward函数反算回原来的vpos.x空间减去vPos.x得到每一个泡泡位置,然后minD=min(minD,len(o)-rad)把这些泡泡融合。 这种算法非常有意思,值得参考。其次是泡泡的渲染,trace十次得到一个10bounce的反射结果,其中raymarch在内部时候同样是取反normal实现内部反射,光照部分,reflection按照10000,100,10的指数累加pow(NdotH,指数)再加上skycolor,获得多层次的高光值得参考。最后把计算得到的fresnel再乘上FilmColour, colour+=reflection*fresnel*remaining;remaining*=(1.-fresnel)获得一次bounce的colour和remaining剩余透射率。

这是一个简单的使用fract获得格子guv,使用sin(length(uv)+time)获得全局周期扰动s,并用smoothstep(s,s+delta,length(fract)-r)得到律动的圆格子和color调制的效果

一个快速的vec4 h41(float)根据时间切片floor(t),mod(t)传入的片断时间,调用4次产生4个用于身体各部位运动和特征的vec4随机数。一个line函数用于绘制线段,一个stride用于脚和脚趾toe的步行位置计算,getTriPoint获得等腰三角形第三个点用于膝盖点的计算,整个流程是先根据4个vec4随机数计算人物特征参数和运动参数,头的大小,腿的长短,各部位摆动方向等,然后根据这些参数计算出实际位置,最后一个个line着色后叠加到color.bufferB用了一个查找邻近像素的方案进行描边,Image画动画背景叠加bufferB。

2d空间变换的demo,inverse square,archimedean spirals很有意思,circles&squares可以用在一些扰动噪声的地方加入octave不知道什么效果。这个demo作为一个线框格子的框架,可以复用。

这个火焰效果代码清晰,是对domain扭曲非常好的应用。用的是优化版的snoise(simplex noise),noiseStack是一个cascading函数,根据不同的octaves(小于等于3 )叠加不同次数每次放大一倍同时有一个falloff参数用于在两个snoise之间插值。,noiseStackUV调了两次不同偏移的noiseStack得到随机uv偏移量.整个效果分为三部分,火焰,烟雾,粒子,flames用的noiseStackUV,octave设置3调的noiseStack得到flame的noise,然后pow(noise)并根据两边和向上淡出调制而成,值得一提的是flame最后的颜色是vec3(f,fff,fff*fff)的形式fff就是flame。 smoke用的flame的UV调的snoise然后和flame的noise按比例调制。本作有意思的部分是生动的sparks,sparkCoord用的noiseStackUV,对扭曲后的uv打格子编号,根据编号hash出包括生命,半径等粒子属性,有趣的是sparkModulus的计算(决定粒子扭曲的效果)sparkModulus=mod(sparkCoord+offset,sparkgridSize)-0.5*vec2(sparkgridsize)然后max(0.,1.-length(sparkModulus)/sparksize)得到spark的基本绘制,是对1./length()这类数值压缩巧妙运用。

这个demo中的火星文之前有写过基本原理,他的亮点是在xz平面的格子路径中穿梭,能找出最近的岔路,具体原理基于dda,获得近处和一下个近处的格子,只在这些格子中进行绘制字幕下雨。字母下雨就是一个floor决定字母,fract绘制字母的uv.值得一提的是本案例中有一个3d步进投影到2d的转换算法。ro3+rd3*t3,ro2+rd2*t2,这里用到一个t3_to_t2可以在t3和t2之间相互转化。rd2=normalize(vec2(rd3)),t3_to_t2=abs(rd2.x)>abs(rd2.y)?rd3.x/rd2.x:rd3.y/rd2.y; 除以这个就转回t3了。

黏菌流体的模拟,没有使用voronoi particle tracking,没有插入排序求邻近。把particle的X,V,M位移速度质量压缩在一个pixel里,diffusion和advection在Reintergration里,用于根据diffustion amount对周边particle的x,v,m进行累加获得当前particle. Simulation主要是计算force对V积分。BufferA对位置积分Reintegration, BufferB对速度积分Simulation, BufferC根据邻近计算密度。Image绘制

iq的蘑菇,textureImproved用了一个cubic插值采样textureGrad,蘑菇头是一个sdEllipsoid然后用sincos扰动出凹凸再减去一个用sin(atan(p.xz)调制出伞内波动的sdSphere。蘑菇头有两个head,head2,head2没有内部伞结构只是为了对蘑菇外的白球球作裁剪。蘑菇的杆是一个sdSegment,用sin(p.y)对p调制出歪曲的杆。白球球ball部分比较有趣,在head2的sdf小于0时,对p.xyz 进行floor fract,离散化然后length(fract)-0.03得到和蘑菇头相交的小球球。远处的石头也很有意思,就是对xz平面floorfract打格子编号 根据编号hash出随机的切割平面,然后dot(p,pp)计算平面sdf,用球体d3对平面作smax剪裁。地表和石头都用贴图作了displacement.代码中texcube就是triplanar,用于蘑菇杆和远处的石头,蘑菇杆根据原来的sdf塑型计算uv1,uv2,然后用uv1uv2采一张贴图构建出bn1,bn2两个方向的bump用pos.y插值然后对normal进行扰动形成带凹凸纹路的蘑菇杆。值得注意的是本案例中stachastic dof和sss的简单实现。

这是一个1/2+1/4+1/8+1/16+1/32...的geometric seriers的公式演示,提供了深度图和traversal,可以用在virtual texture或geoclipmap运用。代码中给出了维基的链接,大概意思就是变换到2为底的对数空间,在2底对数空间floor离散化后得到一个指数,用于计算最后的级数数列之和。具体参考维基

这个代码写的实在看不进,BuffA保存的是碰撞检测用的separating面和产生碰撞的点,包含sep碰撞面检测函数等,值得一提的是并没有使用循环,而是通过fragment的uv去变换出cube序号定位cube。BufferB计算的是速度和角速度,会读取每一个方块的位置和force用于计算,角速度需要惯性张量计算出质量来积分,这里用了一个RotationalImmobilityTensor常数是个啥玩意暂时没搞明白,计算出了角速度以及速度保存。BufferC计算force,需要读出每一个方块的速度,并计算出相对速度,这里也使用了fragmentUV代替循环. BufferD初始化方块的物理属性包括四元数方位,位置,速度,角速度。 Image部分绘制并没有使用raymarch,直接循环方块和rd进行碰撞检测。整个例子真正读懂需要去细化一下方块的碰撞检测代码,一些cpu端的物理引擎根适合理解包括惯性张量,复杂碰撞,以及整个积分框架。

2d dda traverse算法,原理是在线段经过格子时,在线段上会产生一条长线一条短线,我们叫做deltaDistX或者deltaDistY,每个deltaDist产生规则是必须有一条直角边是一个cell的长度1. x轴为一个cell间隔时的线段就是deltaDistX,y轴为一个cell则deltaDistY,由于xy上(1,dx/dy)的比例关系,可以推导出是deltaDistX=sqrt(1+(rayDirY*rayDirY)/(rayDirX*rayDirx)); deltaDistY=sqrt(1+(rayDirX*rayDirX)/(rayDirY*rayDirY)), 可以简化成deltaDistX = abs(1./rayDirX),deltaDistY=abs(1./rayDirY); 很容易推导sqrt(1+(rayDirY*rayDirY)/(rayDirX*rayDirX)) 等价于 abs(sqrt(rayDirX*rayDirX+rayDirY*rayDirY)/rayDirX). 然后根据这两个线段累加的长短选择短的那条线获得最近交点,利用三角形相似原理,起点部分sideDistX/sideDistY的计算类似都是乘上abs(1./rd)获得。算法图示部分已经收录

这是一个尝试实现一个hashtable中元素按空间排列的例子,我的理解是这里的keyvalue这个pair和索引是解耦的,相当于一个jfa的种子其中key是当作位置信息,通过作jfa算法在另一个buffer(cubemap的后1024*1024*3部分)让key的元素走到索引的位置。在cubmap前一半部分对key的位置和索引比对验证到了正确位置则保存下来。我不理解的是为什么要多此一部,jfa的空间如果是key空间就没别要jfa了,如果不是key空间那是哪个空间?。本案例中octree的dda和子节点traverse似乎非常简化,值得借鉴。另shadertoy的cubemap是浮点的1024*1024*6, 用了一个函数raydirToXYFace来变换到fragcoord,再用这个fragcoord得到降维后的idx, 本作的jfa实现比较独特,在一维空间,值得参考.

草丛是用noise2的wind_displacement按草高度因子扰动pos.xz,然后用noise塑形草丛. 由于trace草丛对p.y-h的差值作步进留了误差,这样trace到地形内部获得草的深度也就是和地形的差值,用深度计算occlusion。由于先trace的地形,再地形的trace点再开始trace草丛,因此保证了速度和精度。

首先利用1/2+1/4+1/8..geometric seriers公式,转换到对数空间离散化再变换回来获得按对数倍率对齐,在每一个离散化的位置v0上绘制一个quad,一个quad由两个三角形组成。由这些quad可以组成一个16个格子的patch,代码中有图示,注意patch中点按dess2*4的fract计算获得和v0没有关系, v0只是用于绘制网格,然后让patch中心点最近的周围一圈顶点向中点C靠近。靠近的权重由dess/dess2给出,dess是非对数对齐的坐标,dess2为对数对齐后的坐标,因此总是在lod的变化位置也就是对数的对齐点权重最大。这样就融合了不同lod的mesh

地下城的生成算法,基于简单规则,根据四周决定当前像素内容。真正的运算分辨率是左下角的大小然后渲染的时候放大到屏幕。整个算法根据frameCount执行几个generation流程,主要是room,tunnel,expand,existroom,clearup,room按概率随机设置room像素,expand对这些room像素进行扩大,existroom设置出口,然后tunnel根据出口方向作延申并标记出方向用于下一步的tunnel生成同时会根据四周像素和弯折的概率改变生成方向。cleanup最后一步清除一些死路。最后放大并根据cell和cell周边内容进行渲染。整个demo的亮点是过程化和渲染风格不错. 代码可读性一般,仔细看产生的地下城不是太细致和合理。

基于WFC的一个demo, 地形房子草地湖小路这些内容以及他们的方位都编码到一个int m, 尝试次数try在另一个通道都编码到fragColor中。按照一定间隔对buffer布置随机内容值作初始化(iframe==0),读出当前的m和try值(如果m是空则给一个随机值)4个邻居N(N.x),和他们的try值N.y,然后按照一个RETRY数目不断去check()当前的m和邻居在他们的方向上是否匹配,如果任何一个有内容的邻居没有匹配上并且他的try数目达到上限,则我们要给当前的像素m设置-1用于下一帧继续以随机值去匹配,如果m一直是-1同时周围有内容的邻居超过2个,那么try计数增加。成功匹配try则置零。 然后每帧都执行这样的过程. wfc的核心就是设计check函数,算法类似回溯,如果邻居中有超过try限定还没有匹配的那么自身也需要重新随机。 Image部分渲染读取m使用了一些sdf图形函数进行渲染.

marching cube的简单demo,和作者之前的demo一样都是基于voxel dda去trace。marching cube的基本原理: 一个cube8个顶点在isosurface内外的情况共有256种编码到cubeindex,用这个cubeindex查找edgeTable和triTable获得编码了的边和三角形. triTable给出的其实是cross edge,用这个cross edge序号去查vertlist获得正确的三角形顶点。vertlist保存的是12条边上的顶点位置,如果是cross edge,根据顶点和顶点所在的isosurface的sdf值插值计算这个顶点。

通过改变cell的形状,产生不同效果的surfacenet 的demo,同样是作者之前surface net的框架,事先把计算后的顶点和crossedge信息保存在buffer中,image部分只需读取buffer,不过由于渲染部分要多读几个voxel并绘制shape,帧率不是很高。

这是一个在fragment中绘制模型的算法演示,有点晦滞难懂,不过大概意思就是把兔子模型的顶点,边,三角信息按照索引关系保存的同时和屏幕空间作联系(投影),最后通过屏幕空间去查的这些三角面,然后绘制。这样的算法总体来讲肯定比直接对3d polygon进行sdf要快得多,但是使用较多buffer,而且视角一旦变化和屏幕的 索引关系可能又要重新计算,直观上我认为我很难深入这样的算法。

这也是一个效果封神的顶级神作。城市生成用了一个kernel根据周边像素迭代而成,类似迷宫生成,非常值得赏玩。渲染部分都是基于voxel的dda,首先是march地图,通过之前计算的buffA高度图叠加ease弹性效果作为heightmap,每次dda march步进都是和这个heightmap以及voxel宽度组成的aabb作碰撞获得trace点,然后还有一个marchlight,沿着灯光方向步进但是碰撞后不返回而是累积density或者说opticalDepth,最后在mainImage中沿着视线方向按透明度叠加因子累加通过march和marchlight计算得到的光照信息,整个过程在AA双循环内,并且Image里还作了斐波拉基DOF ,虽然对场景包围盒作了碰撞过滤,但是要达到最佳的效果帧率也有限。

continous lod的演示,原理和作者之前的那个20200813~172116原理是相同的,其实也是一个16个格子的patch,让cell的大小及location位置随着lod的变化而改变,lod是log2-floor(log2)得到的0~1的值用来在两个lod边界中间作为权重改变cell和x00(左下角location)的大小和位置。随后以这些新的cell和x00去构建cell采样isosurface的sdf 用marching quad找到线段并计算出真正的线段。按照marching quad/cube的原理,由于这些线段本来就是需要插值计算获得,随着cell的vertexs的sdf值不同而改变,因此cells虽然变化,但是cell变化的连续性能够保证线段的连续性。这个demo应该可以扩展成3d版本。参考价值较高。

surface net的算法演示,demo中的voxelcache部分的坐标转换来自于另一个mincraft demo的作者,大概意思是以玩家为中心的voxel坐标系排列存储在texture的左下角并且是斜角对称的索引,加载voxel可以从玩家中心开始。回归正题,surfacenet的原理,也是每一个cube的corner去获得sdf的距离,任何一条边的两个corner的sdf符号不同说明是和isosurface相交的cross edge,记录下来,同时要计算交点,方便理解可以假设在2d,并且假设isosurface这一小段是类似平滑的直线,我们要求cross edge和这个isosurface线段的交点。 由于两个端点的sdf值就是分别到isosurface的垂线距离,按照三角形相似原理,就可以计算出交点了,再回到3d,需要把这些交点相加平均成一个点。对于那些非在cross edge上的顶点并不是我们关心的。由这些点组成的quad,需要根据cross edge和edge所在的正负6个axis方向参与计算获得。具体可以看代码或者已经收录的一篇surfacenet文章。另外本demo使用的是voxel dda的 trace,也是个很清晰易懂的范例.

这个作品的毛毯塑形是非常赞的,值得学习,毛绒效果其实是一个cloth的噪声制作的bumpmap对normal扰动一下,光照使用了只有D项的specularBRDF,一个预设的SH作环境光,自己打了一个间接光,一个直接光(ndotl). Image部分用了一个根据周围像素明度作的降噪。

这是一个不用分形迭代只是用一个变换矩阵对0位置进行变换获得螺旋上的每一个点。spiralCenter公式只是用来计算螺旋center,和螺旋本身没关系。 原理代码中注释写的非常清楚,根据translation,rotation,scale三个参数计算出a和k,a是第一个点到螺旋中心的距离,k是这个距离线的角度,最后计算出center。

这又是一个神作,要理解他的代码,首先要理解在polar空间关于角度,长度,向量,旋转。(注意不是笛卡尔空间),polar空间的向量是笛卡尔空间的角度和长度,因此polar空间的向量的scale等于在笛卡尔空间作螺旋运动,polar空间的旋转角度在笛卡尔空间来看相当于投影到helix螺旋线上(只有在螺旋线上才能保持相同的比例),基于这样的认识 就不难理解核心函数closestPointOnRepeatedLine了(基本都在polar空间作repetition)。第二个核心函数是pModHelix,用于把笛卡尔空间变换到Helix空间他的xyz就相当于螺旋线上的btn三个向量。pModHelixScale就是在helix空间作缩放,pModHelixUnwrap负责最初的笛卡尔变换到helix空间会按一定比例和笛卡尔空间混合让整个空间有一种扭曲感,这个点子想法非常值得借鉴。整个场景相机视角没有变化而是map的时候对p作了旋转。 unzipHelixModel会根据unzip时间混合进一个sdbox让unzip瞬间产生方快状的效果。渲染部分,主要是outline的渲染,前一个深度和当前深度按比例混合计算出alpha,用这个alpha把主体螺旋结构以及边界作混合而成。

iq的一个domain repetition的例子,对p用floor,fract打格子获得格子id xi和局部坐标xf,然后用一个三层ijk的循环在九个格子内绘制(因为由于domain作了扭曲的关系,三叶草会跨格子),圆柱的圆心在0位置,因此直接就可以算出三叶草的朝向,构建wwuuvv三页草坐标系,稍微做一下扰动把r变换到三叶草空间r,然后shape(r)得到sdf, 再和树干min融合。其中原本的r是格子空间的r。trace和绘制部分没啥好说的较为传统

又一个巧夺天工的神作,本作的螺旋公式以及空间变换的参考学习价值较高,首先r=exp(b*sita),b为指数放大的常数决定贝壳的半径,然后变换空间开始, p.xy绕a旋转,a由length(p.xy)决定,因此xy平面被变换成螺旋空间,t=atan(p.yx)获得sita(角度),然后是u是longitude的t,dx是ring中心exp(b*PI)在xy平面向外辐射的距离,dy=p.z是厚度距离, d是到ring的距离有点绕len(vec2(len(p.xz-vec2(-ro,0))-ro,p.y)),就是个空间长度计算。d和dx的区别是,dx是xy平面到ring中心的距离,d是绕z螺旋旋转后的xyz空间中到ring中心的距离 然后需要参数化螺旋结构获得具体的结构位置用于绘制,spiral公式r(n)=exp(b*(2.*PI*n+t)),(x-r)^2+y^2=r^2 然后去求圈数n,这里注意的是公式中的xy填的是p.z和t. 在用floor和ceil获得内外两层n0,n1, 用exp(b*(2.*PI*n01+t))螺旋公式获得n0,n1处的半径r0,r1。然后在ring的内圈外圈上更新u,if(d0小于d)d=d0, u=2.*PI*n01+t;,有了这些参数就可以计算后面壳内的结构以及着色处理。他的cut用了d=max(d,max(cut.x-p.z,p.z-cut.y));让前后两面随着cut参数变化对螺壳作裁剪。

给出一个俯视sdf,一个y轴上的旋转切削半径图(类似车床,不知道用什么词),shape1,shape2, sdf=shape2(vec2(shape1(p.xz),p.y))得到3d的图形,这个demo中有较多的sdf函数值得参考学习,譬如旋转45度pR45(p) p=(p+vec2(p.y,-p.x))*sqrt(0.5)这种写法非常少见,另,他的着色是是根据y的高度,因此屋顶的颜色不同。

iq 的一个早期作品的shadertoy移植。 代码简洁易懂,但不代表内容简单,只是又一个代码艺术。噪声使用的fbm,butterfly是一个蝴蝶纹理着色函数,对p空间左右abs,分为翅膀的上半部分和下半部分绘制和用alpha叠加(上部翅膀叠加在下部翅膀上),同时输出的alpha也是蝴蝶的轮廓。纹理部分主要是变换到atan空间通过辐射长度和角度调制fbm获得纹理,一些线条部分用pow压缩sin获得。 然后是stateless翅膀运动部分,主要是翅膀的角度根据时间变化,由于是非线性的,所以通过对时间函数积分获得正确的运动角度,而这里因为用smoothstep插值获得角度,所以给出了smoothstep的积分,非常值得借鉴。然后是raymarch,由于就左右两个平面,作者写的很简洁直接求交点t,pos=ro-rd*dot(ro.xz,pl)/dot(rd.xz,pl),其中pl是平面法线(xz),通过刚才的smoothstep积分函数获得,然后q=(length(pos.xz),pos.y)作为蝴蝶的纹理 进行绘制。值得再一提的是他的dither效果,是把AA,time都算进去了,因此会有一个动态模糊的效果非常不错,time叠加了一个sin(p.y),让翅膀产生上下部分运动层次。

主要的塑性函数是spiral包含了底座部分。t是时间或者旋转角度theta,m是圈数或者repetition数目,首先根据m和t计算出中心位置对p偏移,然后按t再对p作旋转,旋转xy空间后再转换到polar空间,p=vec2(atan,lenth), 然后在这个螺旋polor空间对p.y按m作repetition,就可以计算出a=abs(p.y)-size,螺旋的sdf了。接下来就是根据螺旋的t,repetion前的p.y(卷的半径)以及p.x(atan的theta),计算长条最后和spiral作min融合 spiral1,2,3应该是同一个条上的三个卷。其余部分基本上都是修饰性代码,没有仔细看。总的来说本作的螺旋实现上简单易懂,但我原本以为他真的把一个长条作了螺旋扭曲,那样的算法通用性上更吸引我。

好像是kali的早期作品,分形星空背景并没有用噪声,而是随时间和p.z的cos变化调制空间同时获得depth,再用mod和时间调制处折叠的空间,然后p=abs(p)/dot(p,p)-.9作6次迭代叠加len(p),再和depth调制而成。堪称神迹。然后是thorns刺的塑形,用了len(p.xy),p.xy=vec2(fmod(atan(xy),0.15),fmod(atan(yz),0.15)),xy平面的角度和yz平面的角度组成一个球面折叠空间,s1,s2,s3是用smoothstep对刺的范围作了调制,简洁优雅值得学习。skin的塑形用的星空的分形,finn鱼鳍sin(atan())在不同平面调制出波浪形状 嘴巴的塑形就一句,max(-p.x,mo);mo=len(p.yz*vec2(ani_mouth-p.z,1.))-.02*ani_mouth; 在yz平面根据动画和z调制出侧面嘴形然后len-.02*ani_mouth是获得sdf。最后和-p.x作裁剪。这种技巧非常棒。值得注意的是他有个spirl函数对整个场景作了一定扭曲,实现也非常简洁值得参考。最后整个场景并没有雾效和bloom,而是根据trace的dist调制的颜色。这是kali大神的一贯特点。

非常好玩的一个毛发塑形,首先是p+=sin(p.yzx*sin(time))对圆球周围空间进行扰动,然后获得r=len(p),扰动球体的sdf,接下来是r2毛毛的sdf,核心部分是v,类似triplanar的法向权重,得到的是vec3到vec2的球面映射,然后在这个2d空间作mod折叠v,最后r2=len(v)-0.03获得毛毛的sdf,和r作min融合。最后值得一提的是raymarch部分,会先作一次trace得到一个t作随机扰动,相当于volumetric中常用的jitter,避免层纹。

这是一个非常棒的使用keyframe的动画框架,最终的keyframe是在三个不同状态idle,attack,aggressive之间作插值获得,关键帧插值对ratio作了cubic平滑。然后有一个食人花的结构体预先把计算出的各关节部位位置保存下来这样后面sdf部分就比较简洁了。塑形部分值得注意的是3djoint和他的matrix的生成,有较高的参考价值。 头部颜色用了dir=vec3(0.,len(p.xy),p.z)转换到球面空间,然后用atan(p.xy)*pi/ps.mouthAngle得到张角对球面空间作变换,然后sdf+=smoothstep(dot(dir,somepoint))获得有被张嘴拉伸的点状效果。注意他dir.xz=abs(dir.xz)是让点更多省略somepoint编写。塑形部分用了个sdCappedTorus变换折叠空间作嘴唇,然后sdroundcone当牙齿左右对称,中间一颗在对称平面上,所以需要绘制5颗,天空部分作了一个skyDome函数用atan(xz)查询,云是几个noise的octave作了cascading return

球体本身没有旋转(sdf函数内旋转)而是对trace得到的point旋转然后fbm获得纹理。纹理部分用fbm函数扰动uv叠加p再去采样fbm。蓝色月球上的atmosphere通过计算horizon=sqrt(dot(monCenter-ray*d))然后pow调制插值颜色而成。飞船没有着色,只是计算reflect,然后当成ray去trace,ring部分就是个plane的sdf和fbm调制而成,整个案例简单主题鲜明。

iq的早期作品,整个恐龙的塑型基本就是椭圆形+smin,值得注意的是base函数构造tbn坐标系在塑形中使用是很方便的技巧,尾巴用了bezier,膝盖处用sin调制椭圆,爪子部分因为只有3个好像,就用了两个椭圆叠加,其中一个在对称空间,最后smin通过参数留出缝隙.云是plane的trace然后采noise,terrain是用几个纹理图调制而成的高度图。值得注意的是demo中的improvedTexture插值采样和dpdxdy在采样中充当lod。相关iq的filter推导已收录本笔记。

这个场景特别的地方是雪地部分的微观形状,其实是calcNormal的时候传了个t进去,t会根据屏幕分辨率算出一个pitch因子,关键的部分是用这个pitch因子缩放求梯度时的中心offset的距离,并且他用的是tetrahedral offset,应该对形状也有一定贡献。调制offset距离而不是直接先计算grad再扰动,这种方式是把视距考虑进去避免远处出现高频的filter方案。雪人塑形主要是男女头部,主要用了sdTorus用abs(cos(atan(p.xz)))作了扰动。

这是一个效果超级赞的demo,主题其实是利用reintegration tracking算法对山脉作erosion,山脉的基本塑形是noise2d然后octave再叠加bufferB的erosion高度因子保存在第四个通道,BufferA的前三个通道是normal信息用于后面粒子的积分,BufferB主要就是作粒子的reintergration tracking,根据BufferA的山脉grad调制像素粒子的速度,如果没有到达水平面的高度就对像素的xy作integration。前半部分的reintegration tracking,根据周围粒子的移动方向计算出落点格子如果和自己的格子发生碰撞,取一个比例把周围粒子的速度加权到自己像素,随着每帧迭代像素粒子就产生了移动。 整个过程作者有写过一个专门的reintegration tracking demo用以演示。这次用在terrain erosion上辅助山体的塑形,本作使用了大量的intersectBox,对云,水体,山脉作包围盒检测,顺便作裁剪。BufferC主要是渲染部分,TerrainBump给出山脉的小细节。 云体基于Noise3D的density和对light方向积分和exp(-density)衰减叠加,水体是一个noise2D计算grad得到refrect。AO部分就是获得高度落差作为occ因子. 本作内容丰富具有较高的学习价值。

tiling函数的集合,包括六角形,三角形等,重要的是借鉴价值较高,除了返回uv,还有中点,边界,id。

这个普通fresnel+specd,然后用edge=smoothstep(0.,.2,dot(v,n))插值mix(bgcol,color,edge),最后反射采样一个卷积过的高光,居然获得珍珠的效果,edge计算本质就是个rim color。光照计算被写在碰撞检测函数内,作者用了个inout color,这样的框架可以方便的绘制任何形状。

这是一个内容丰富的3d particle sph在玻璃容器内的运动演示。fenix的作品,之前有分析过很多他的demo,他的作品主要以3d粒子运动模拟为主,3d particle的voronoi tracking 框架,用ssao/gbuffer渲染等。这次演示主要是3d粒子sph fluid模拟,次要部分光追框架也值得参考。首先是common中一些常用的四元素函数用于容器的角动量随鼠标变化的计算,particle结构和存取。BufferA是sph粒子物理模拟核心部分,根据邻近粒子密度,位置计算sphKernal,presure,density,force,vel,最后对vel积分v=v+f,再对位置积分s=s+v/density,其中会处理和容器碰撞部分,对vel弹性和limit处理等. bufferA后半部分是insert_sort邻近粒子计算会有一个象限过滤算法,BufferB是和屏幕像素建立关系的voronoi tracking部分,整个tracking部分和fenix之前的作品雷同不多表,BufferC就是一个简单的光追框架,迭代计算新的反射/折射线,这里值得一提的是他用一个particlecache函数对粒子和邻居作了smin融合。(因为sphere碰撞检测函数没法作sdf,所以需要缓存位置信息).光照部分就是fresenal,spec,refraction/reflection的计算,这边用了一个alphablend函数在迭代框架中对color作叠加。image就是copy。

这个demo中有大量的soft min函数值得参考。本作的blend用了个expmin, 主体塑形就是两个roundedBox在顺逆时针两个xz方向随y旋转然后blend,最后对d累加一个voronoi 偏移。光照部分ao,ref,back,softshadow等参考iq的光照。

这个例子主要是运用iq的simple IK计算膝盖和肘部的位置,其他的位置根据terrain(timefly(t))获得的路径位置以及前后位置计算每个部位的相对旋转和位置。脚步的位置用fract(time)在floor和ceil两个离散位置的pasaR,pasaL函数插值获得, 球体的运动也是用的timefly,所有位置计算完成后,计算sdf,主要是sdsphere+sdcapsule,光照部分参考了iq的光照风格。

这个案例适合小朋友练习,绘制基本由circle和rect组成,基本没有融合裁剪,所有的sdf都作了0或1的step,手部是几个sincos定位的rect。

这个covid病毒案例也是自2020年以来非常出名,塑性部分主要是inv_spherical_fibonnacci,iq的费波纳基球面逆函数。然后获得临近刺的位置,用这个位置和圆心构建tbn,basis是一个球面tbn的构建函数。然后把空间转变到刺空间,在刺空间用rot2d对p作了一下bending,这个函数叫做get_corona_params(p),返回的是刺空间,整体的map就是在这个刺空间作sdline,smin,smax,sdsphere,然后是值得一提的是内部的球体bummap是用了texcube2D对p(这是还不是刺空间)作扰动返回sdf,而不是直接加在sdf上. raymarch部分用了bisect二分查找值得参考一下,但是他的lighting部分又调了一次map比较费,除了normal调6次map,对获得的n还要调一次bumpmapping作扰动,有较多的冗余代码。背景部分就是个fbm噪声。最后tonemapping,image加了dof。

这个pacman作品亮点很多,整个场景的trace是基于dda进行步进,但是对步进分了三个阶段,一个是在voxel内部,一个在voxel内但在roundbox外,一个在voxel和roundbox内,不同阶段步进距离不同,在voxel内直接就用fract(p)绘制roundbox,在voxel内在roundbox外的情况就需要区分是画voxel(roundbox)还是普通sdf,但我觉的似乎没必要分三步。pacman的塑形部分嘴巴的张合先是通过time确定mousthAngle开合角度,然后通过dot(p,vec3(0,cos,sin))获得两开合平面d,min融合后在对body作max裁剪。 ground就是sincos调制.ghost的底部裙摆用cos调制。值得注意的是由于步进中直接使用floor,fract(p),因此在塑型部分对整体的空间进行放大后计算的sdf最后返回原来的坐标尺度。渲染部分,glow的渲染是根据一个glowCounter(步进计数)来调制,也就是步进越多ray越靠近边界。normal分为两部分,fract内roundbox的部分,普通的非voxel部分。包括sdf计算都是通过一个modeVoxel来区分,modeVoxel由屏幕空间划线获得两边的mode区域。颜色部分把sdf中vec2的返回值d.y通过hsv2rgb转换处rgb颜色。然后spec,abiment,shadow,diffuse,distfading处理光照

这个毛发的例子主要是通过层叠alpha混合的方式实现。首先是计算furDensity,通过cartesianToSpherical变换到球面空间获得uv的,用这个uv采样噪声贴图获得微小的density扰动,然后根据毛发长度,毛发的根部到顶端density越小。然后根据furDensity计算normal(有点类似volumemetric),最后的是furShade光照计算,只考虑diffuse,spec,其中spec随毛发深度调制越小。scene进行raymarch,先intersectSphere,从交点位置开始循环采density作alpha,alphablend累加furShade直到density达到一定阈值跳出。值得注意的是这边的blend用了premultiply alpha.

这个作品主要展示了物理角动量模拟,折射渲染。首先是结构体s,s.p,s.q是水平面上某一个pivot位置和目标位置。通过鼠标运动改变s.q,然后s.p插值到q. s.pr,s.qr是旋转角度和目标旋转角度,同样s.pr会插值到s.qr通过slerp(四元素插值),然后根据s.q,s.p计算加速度a,同时通过s.pr角度把加速度变换到object坐标系。然后是T=cross(s.v,a)角速度s.v和速度a叉积获得扭矩T,并按比例累加到s.L角动量。最后用角动量s.L计算出角速度s.v(角动量旋转矩阵和s.v角速度相乘获得对角速度的积分) 这个过程不难理解,需要回顾一下物理概念。然后是本例中的水折射,就是一个标准的光追流程,每次迭代根据碰撞媒介玻璃,空气水,决定折射率ior,计算折射线用于下一步step, 根据h,v,n计算fresnel,spec,refecet,最后把每一步step获得的光照作叠加。值得注意的是他的refractFix函数是refract函数的修改版把total internal reflection返回0的部分改成了reflect。本案例的塑形部分很简单,smin,smax。water部分就是一个法线是s.v,planePoint是s.p+depth*s.v的平面剪裁掉玻璃容器部分。

这个demo令人吃惊的艺术气息来自于教堂sdf和分形carpet的完美配合。值得关注的亮点部分一个是分形,一个是光追框架,首先是分形。利用分形公式uv=1.-abs(s*fract(uv-0.5)-s*0.5);uv*=rot(i)不断的迭代uv,其中s是缩放值。让uv不断的镜像和旋转并且octave。然后在uv上画helix,通过hsv2rgb(i,seed)着色并alphablend。塑型部分通过一个arch的2dsdf构建roof和window,sin(atan(p.xy)+z)构建pillar柱子的螺旋,tilebump函数利用mod(floor)tiling空间fract获得inTile然后max(inTile.xyz)得到tile的bump。 光追的框架很灵活值得借鉴,首先是纹理color是通过各个sdf函数返回省去了记录材质id重复算uv等。然后是有两个trace,tracelight。首先trace和tracelight一下获得base position和lighting,然后在base position上做循环4次根据随机半球面法线继续trace和tracelight累加volumetric和diffuse的light,其中trace部分获得window的color用来着色volumetric,volumetric部分通过tracelight实现,tracelight会根据太阳位置contribute(类似pdf),地板contribute,ambient的contribute 然后概率公式计算出有优先级的半球面采样dir,这一块值得学习。 最后用这个dir去做trace得到的颜色乘上sumContribute(sun,ambient,ground的contribute之和)。最后值得一提的是透视投影部分用来cylinder project改变了下rd,使得视角呈现广角的效果,最后image对颜色除以sample数收敛,然后做了tonemapping,gamma correction,exposition,vigneting,colorgrading.几句话的事情。

这个马里奥的作者应该也是食人花的作者,他的代码的亮点是keyframe动画框架,利用noise(id),noise(id+1)获得前后两个keyframe做mixframe得到当前的keyframe,用这些keyframe数据(主要是表情动画,嘴角度,眼睛张开程度,面部扭曲,摇头角度等)计算出matspace结构用于sdf的各部位位置信息,其中他用降频的perlinNoise代替sincos做眼部等往复随机运动值得学习。塑形部分大量使用了ellipsoid,嘴部和耳朵用了torus,牙齿是cylinder。值得注意的是字母M的sdf只是abs(x)后用了几个max(p.x-p.y)也是值得学习。 最后光照部分,softshadow参考了iq的改进型softshadow(已收录),fresnel,diff,spec计算。值得注意的是pow(spec,roughness)这种写法值得借鉴。最后是背景部分用了noise和sin调制出一个电视噪点效果。

这个作品的重点在云的塑型和渲染上。BufferA是云的Base形状以xz平面为主但是会受norY(云的y轴)影响最终通过三个通道的随机数调制出density。bufferB是32*32*32的clouddetail以slice z轴的形式保存,每个slice都有padding,这里主要用到tilableVoronoi噪声为主,BufferC是Terrain的塑性和绘制主要由普通perlinNoise做Octave获得。渲染上雪地区域通过normal判定,有一个shadow是dot(Sun_dir,normal(lowOctave))计算(其实就是个attenuation).然后ao和heightfog. bufferD是云的渲染。cloudMap用cloudbase形状和detail形状通过一个cloudGradient(norY)调制,云的底部才会叠加细节。然后就是renderClouds,标准的volumetric rendering, 其中volumetricShadow是光线方向采density叠加exp衰减的积分,主循环是沿着视线方向对lighting做累加和衰减,其中乘了一个scatering,scatering是通过HG相位函数在前后两个方向根据sundir计算出散射度,transmittance小于阈值跳出循环.Image部分叠加山脉和云。最后值得注意的是本作使用temprol reproject AA。用前一帧相机算出屏幕位置扣除抖动offset,采样出oldColor和当前color插值替换。 另外本作的lensflare值得借鉴。

这个多肉植物的塑形是作品的亮点。leaf是叶子部分的sdf,根据cellData所在叶瓣位置和bloomSpec开花的形态这些参数决定。一个celltime决定curve形状然后旋转p.zy,计算出slice最后abs(slice)-thickness。其中有两个切割平面smax削出侧面的形状, caclCellData似乎是变换到花瓣网格。mBloom就是整个花的sdf.需要做一个3x3循环opU(leaf)是因为leaf sdf超出了自己格子范围。光追部分有两个march,一个获得hit进行着色,一个在sundir方向计算shadow和直接光照,然后半球面随机出新的rayDir循环叠加算出的shade(hit),shade主要是纹理。整个过程似乎没有spec只有diffuse. bufferB除以sample数收敛pathtracing. image部分值得一提的是一个colorgrading可以借鉴。

这个作品的亮点是暴力multiscattering,还有一个马赛克风格filter让pathtracing显得不那么枯燥。亮点很多。首先是ScatterData结构,保存了scatter散射率(1m),tint媒介颜色保留比例(一米距离散射后留下的颜色的比例),emission是每米积累的直接光,scatterTint散射颜色,eccentricity是scatterRay和lightRay偏离的因子, 媒介和媒介之间用了一个Mix函数做插值用于SampleMedia这个函数中,SampleMedia(pos)直接包含了云,地面,torus,sphere的sdf,然后用他们的sdf插值对应的ScatterData类获得当前媒介的参数,这种做法非常值得借鉴,再也不用ifelseswith了 有了SampleMedia然后就是重点光追了,每个像素做一个seedRand得到jitter算出jitter的ray,从campos开始150次迭代,其中step会做一个随机避免层纹,根据媒介的scatter概率开始散射模拟,其中根据一个directLightProportion比例会一部分bounce给到直接光直接返回直接光的方向用于后面的直接光散射迭代。剩下的就做球面方向的散射但scatter.eccentricity会决定散射方向(偏离前一个方向的程度),整个过程中累积tint,tint*=scatter.scatterTint,累积emission,emission+=tint*scatter.emission,累积bounceCount,计算新的pos。如果是之前的直接光概率 就跳出循环进入直接光的散射迭代。和之前的散射的区别是ray这时是lightdir不再随机,同样需要每次step计算sampleMedia(pos),要根据媒介的scatter率和tint计算absorption,再调制当前tint,最后累加emission,emission+=lightColour*tint; 流程最后fragColor+=emission;fragColor.a+=1; Image部分的重点就是有一个Mosaic filter,根据fragColor.a采样数和预设的target,sqrt(target/fragcolor.a)确定一个kernelSize. 在kernelSize范围内的fragcolor全部累加,最后除以a. 用以收敛pathtracing.这种马赛克方式非常有趣值得借鉴。

章鱼的例子非常经典。一根触须tentacle的塑形是xy平面,x=abs(x)左右对称,scale缩放因子随着y变化,然后sdCapsule画触须主体,p.z=abs(p.z);p.y=mod(p.y) 在y方向镜像画吸盘也是个sdCapsule,最后用sdsphere做个裁剪形成凹洞,然后smin和tentacle主体融合。完整的章鱼sdf会在xz,yz按时间扰动,在xz平面按角度镜像a=mod(atan);p=cossin(a)得到八根触须,身体部分是几个sdsphere融合同时扰动一下空间再sdf上叠加个displacement纹理。渲染部分,值得注意的是bokehlayer函数画几个模糊的圆和water的background叠加模拟bokeh效果。water函数就是根据viewdir.y插值 深浅两种海水颜色作为background, 光照算了个pow的fresnel,叠加了个water(reflec)*0.6, 用一个dotVN在color和background(water)之间插值获得rim效果,最后vegnette。

这是一个13年的作品,有很多神奇的代码。field函数通过floorfract和len,sin(在格子内把圆累加)产生一个2d plasam,用cubem把field变成cubemap采样, isosurface是一个sdf函数,与以往不同的是使用1/exp当作union, exp+exp+exp当作intersection。shading部分会传入一个visiblity(相当于transmitance),pow(vn)计算fresnel,最后fresnel插值albedo*(l+f1)和specular,其中l=lightcolor*visiblity。然后是subsurfaceTrace函数通过沿着光线方向步进计算visiblity(subsurface),最后shading会用一个Humbug函数包裹。Humbug负责trace+shading,同时inout参数累加result,返回refect计算后的pos。result.a保存fresnel,这样Humbug可以调用多次反复光追并通过fresnel按比例混合subrefract和reflection或background

这个极光模拟非常经典,aurora函数循环50次在(pow(i,1.4)-ro.y)/rd.y按几何级数分布的50个平面和视线交点处绘制noise并exp2(-i)按层衰减avgCol同时累加到col从而产生衰减条带的效果,star会和aurora做一个blend叠加。alpha因子由aurora中triNoise2d提供。star通过fract,floor画格子step(hash)实现。倒影部分通过rd.y=abs(rd.y)再画一次aurora,star。

这个demo演示了voxolize风格的渲染,gridStep是dda step,每次返回最近的n和pos,没有用branchless。根据fracoord.x>fraccoord.y*.4在屏幕上斜着划分两个区域,左边是voxel风格。普通风格就是普通的march+ao+soft shadow。voxel风格循环gridstep获得第一次碰撞(每次用step得到的pos做一次sdf判定)的pos和n, 有两种ao,一种是dist(pos)/grid.x调制,意思是voxel的连接处,一种是dist(pos+n)调制,意思是获得normal方向的dist调制,还有一种线条绘制值得一提,s=sin(pos/grid*PI2*.5); br = exp(-s*s/.05)。意思是只有voxel连接处才是完整的周期使得sin获得最大值,exp压缩获得线条。渲染部分放到了最后一行fragColor赋值。基本上就是diffuse.

iq的3dJoint例子,返回的是sdf和坐标系,不过只有一根轴向。disp在这跟轴向上做sin(atan(p.xz))扰动。纹理部分sincos调制也是用的joint坐标系,使纹理跟随joint产生褶皱.

这个人头作品的亮点自然是人头的塑形,并没有想象的复杂,基本都是ellips,capsul通过smin,smax组成,下巴部分会用一些平面做smax剪裁.上下嘴唇也是椭圆sdf。这个作品的真正亮点是在cube buffer内通过mainCubemap传入的rayDir利用dda原理计算出faceNormal,以及facePoint。然后从cube的面上的点facePoint作为ro,rd=-faceNormal,有外向内做raymarching.保存法线和t. Image部分射线和中心位置的一个box碰撞,从碰撞点出发向内raymarch,用一个pointIsInside(pos)函数做跳出条件。pointIsInside会根据cube buffer中的六个面的t判定是否pos在模型内部. 然后是光照,计算reflect和diffuse. 其中Normal的计算通过一个getNormalForPoint(pos)获得。这个函数通过判定pos在cube的t位置以及每个面t值的比较值来确定pos到底是靠近哪个cube的面,获得一个coord,采样cube这个面获得之前保存的normal. (if语句太多,是不是可以用triplana原理来决定法线权重确定平面呢?)

这个作品的和之前的草地渲染略有不同,raymarch部分用了bisect二分查找,直接用y-grass-terrain做d,这和之前遇到的一个草丛渲染不同(先march地形,march终止距离留一个阈值,在地形内部再去march草丛) ,草丛是一个perlin,草丛的运动通过一个shear函数,随着p.y的变化调制sin(xz),使用了一个特别的AA,当前草丛的sdf会cache到blades[8]的数组里,正真的d是blades取平均(但这显然不是AA),这样的做法似乎让occlusion变得更明显。occlusion和之前的草丛例子一样都是深度因子。渲染部分用了一个fbm着色,exp距离淡出. 背景的山脉是用sin做了octave,然后叠加一个firecolor函数效果不错最后tonmapping

这个bigwings的特斯拉cyberTrunk的例子确实如他所说写的有点乱,灯管是rayline的sdf,绘制的时候根据最近距离着色。并没有arealight,只是一个spotlight的attenuation(1./dot(lightpos)),玻璃上的反光实际是计算了reflect又画了一遍灯管。车辆的塑形部分基本都是定义各个平面然后做max剪切。车轮纹路和轮毂稍微复杂一些mod(atan)镜像一下轮毂部分和橡皮部分,橡皮部分用sin调制处胎纹。内饰部分是再matID==MAT_GLASS时候绘制。车前后大灯的晕出部分其实就是根据brightness/sdf做着色。和之前有一个光管模拟arealight的范例类似。本案例没有使用shadow和任何ao,基本都是着色部分模拟。

implicit隐函数march的范例,本例是一个心型,march算法是dt=func(p+t*d);dt/=length(grad(p+t*d));t+=0.5*dt;,实现部分再castRay直接返回了pow spec +diff的着色,背景部分在dt>max_dist时绘制,sin调制P(P=p+t*d)乘上个vdotl给一点高光

滚动字母桶,亮点部分是变形和字母, 变形其实是两个滚筒通过一个sin(time)调制的blend用opU(d1,d2,blend)融合而成,字母的延时滚动是通过一个dat数组cache时间,map的时候用不同的dat旋转cylinder坐标空间。字母也是在cylinder的uv上通过fractfloor获得。字母突起是和cylinder直接max获得突起效果再和另一个cylinder融合而成。然后ao,softshadow,diffuse,fresnel,spec等iq式光照完成渲染

这是一个玻璃球pathtracing的例子,亮点部分作者使用了一个calcAbsorb函数扭曲球内空间输出一个emision,在calcScatter散射计算的时候每一个步进循环调用,累加这个emi,同时exp衰减m_col(相当于scatter率),同时会有一个概率判定是否是直接光的步进,在直接光阶段计算rd=sampleHenyeyGreenstein(rd,g),g=0.5给出一个前后散射率相同的rd,媒介为none然后返回,非直接光阶段返回的是refractive媒介用与在bounce阶段根据inside标记算refract射线。最后mainRender中迭代128次bounce,分别判断plane的lambertian媒介,玻璃球的refractive媒介和内部的scatter媒介,backgournd媒介是一个调制了贴图的背景光。 同时计算每个阶段的bounce的rd. lambertian用sampleCosWeight,refractive用sampleFresnelDielectric(就是refract射线),标记is_inside。有意思的部分就是calcAbsorb函数让一个背景光在玻璃球内呈现光线被吸收后的彩色线条。

这个例子非常可口,首先是甜甜圈donut的塑形,icecream就是一个sdTorus然后用sin(atan(p.xz))调制出的波浪以及dot(p.xz)中心作裁剪,在和主体sdTorus作opU融合,然后smin三个小球球产生奶油下流的效果,sprinkles小球点缀部分用了两个polarCoords返回的sector来hash出镜像位置然后sdsphere最后和主体opU.渲染部分,值得注意的是calcSSS作者返回了一个sss颜色其中用到了negative diffuse,这种算法值得参考。最后有两个非常值得关注的亮点,transition屏幕效果和frozen效果。tansition用了一个tr=step(0.5,fract(uv.x+uv.y+sin+iTime))当作两个背景色的blend因子。背景部分的条带stripes=step(0.,sin(uv.y+sin(uv.x)+iTime))用两个颜色调制出两个背景,最后blend去插值获得tansition的背景效果。 然后是甜甜圈的冰冻渲染,col+=occ*pow(fresnel,pow(fbm));用pow(fbm(p))作指数调制fresnel。值得学习

这是一个霓虹主题的短片类效果。demo中有较多的sdf函数和裁剪融合等操作值得参考。场景包括地形,路灯,建筑基本都是sdbox,sdhexPrism等。车轮廓是几个plane作intersect再融合几个sdbox,车轮sdtorus.车内饰主要是仪表盘用到了repeatAngle等mod(atan)的角度镜像然后绘制sdRectangle。渲染部分有spotlight,以及raymarch的volumetricFog(包含高度雾,最终计算出alpha调制inout color),特点部分霓虹线条根据sdf获得的材质信息,只是简单的floor(p.y)调制出emision,叠加到color光照运算中。光照部分包括基本的diff,reflection,和specular. 然后有一个tubelight和spotlight,以及terrainAO作ambient. BufferB是一个扫面线的屏幕后效。Image作了chromatic abberation.

雪花生成算法和渲染。common是一些没有sincos的hash函数合集值得参考。核心生成在BuffB, BuffA对BuffeB像素周围6个方向采样任何一个周围像素的x>1.0就在当前像素的y通道置1,o=vec4(v.x,receptive,0,0),可以理解为标记一下密度高了。BuffB判定像素的v.y的密度对v.x累加,如果是1就设置初始值GAMMA水的density(小于1),否则设置初始值为-ALPHA*v.x,ALPHA是0.5~1.5之间。然后v.x再加上四周像素x值的平均v.x+=nrs/6*ALPHA,nrs为6个四周像素.x相加。然后是屏幕上布置随机种子,同时随机一些区域O.x++增加随机性.Image部分的渲染,对UV作一个六边形角的旋转采样中心以及右边和下边像素,这样就算出grad,通过grad偏移出一个uv采样background相当于折射了,然后再叠加ground,再fragColor+=0.2*dot(grad,vec2(1))相当于加了个spec,最后再AA循环render.

这其实是一个草皮渲染的例子,erosion风貌的地形部分由sin(mat*p)叠加而成其中mat是硬编码由第三方软件导出的旋转矩阵,由buffA作了cache避免重复调用。而本案例的关键是bufferB草皮部分的渲染和塑形. treeVolume是塑形部分2*2的循环floor,hash出种子采样heightDensityMap获得密度分布,并在格子之间作插值平滑,返回草颜色和01单值的草坪判定。shaodw部分分了两块,沿着光线方向循环8次采草坪判定a获得树的shadow, 光线方向循环64次raymarch terrainsdf(采的贴图)计算soft shadow. trace函数延视线方向256次采terrainSdf获得交点同时插值计算出td树的高度,其中退出条件fPlane远平面是raybox通过dda算法计算出的远近平面。 第二部分作树的intersection,继续之前的步进td,循环256次叠加oDepth,达到阈值退出同时返回treevolume().rgb树的颜色,接下来就是对terrain着色 通过terrainNormal和fbm调制出albedo颜色,最后trace返回的是(t,albedo,树判定)结构。然后是fog函数,类似的会先和一个sphere碰撞盒求交过滤射线,hS,hE代表距离衰减和高度衰减因子,exp(-)算出衰减值,HG相位函数根据衰减值e,fAnis各项同性参数,视线,光线调制inout color。海面waterDisplacement用heightDensityMap地形和fbm调制出海岸线和chop叠加而成,reflection函数会先计算fog, 计算water normal, 然后叠加foam color,foam会算一下diffuse,skyambient. 水面fresnel, 水下的散射部分通过水深计算scatter因子插值skyAmbient和terrain光照算出反射光线,然后(1.-fresnel)调制叠加到col, 最后再trace一次返回反射光线和t。 来到mainImage,先trace一下获得起始点,然后reflection, 然后再根据之前的trace出的albedo计算树和地形的光照(diffuse,skyambient,树属于volumetric不参与diffuse)作叠加,最后算fog color。最后会取出旧相机数据作一个reprojection aa。image部分作了dof和一个fast bloom ,快捷的写法值得参考。 然后BuffA中有对相机路径插值的部分写法简略值得参考。

细胞自动机生成剑的例子,细胞自动机的原理就是采样周边像素不同方向加权求和得到一个k值,根据k值调制e,返回e。然后e=CA(px,e)每帧迭代自身。然后hasWhiteNeighbour,isInteriorCorner,isExteriorCorner,wasInDiagCorner,isIsolated等这些采样特定周边像素的判定函数按frame顺序被依次执行。这个过程有点类似之前的一个迷宫算法20230129~213249,本质都是细胞自动机,Image部分通过硬编码调色板进行着色

一直觉得这种效果很赞,代码中s,r,n分别为ro,rd,normal的全局变量,全局变量说是为了省空间?。主要的塑形部分是box函数,根据不同的sceneid,设置不同的镜像参数rep,然后floor出boxid,boxid会根据不同的sceneid调制出不同的旋转角度a,然后对box作射线碰撞获得d,n,过程中box会被变换到axis aligned轴对齐再作碰撞,最后再把获得的n rot(-a)转回来。trace函数就调用box,cylinder,然后s+=r*d计算最后的碰撞点用于下一次bounce. mainImage先作简单的dof,然后循环3次调用trace,计算光照累加color。其中仍然会根据不同sceneid计算不同的灯光,用于reflection的roughness, roughness变换成cone随机向量扰动反射射线。Image部分主要是bloom,tonemapping,然后对短片前后时间段作fade.

P_Malin的血撒南瓜shader,整个代码注释和框架清晰,很好的学习模板。Common里是四元素,相机变换. BufferA为渲染主体部分,包含了相机类型flycamera等的updata。SceneResult场景结构包含vUVW和ObjectID以及dist.然后是一些sdf bool操作函数。trace,traceSoftShadow,traceOcclusion。一个SurfaceInfo结构包含pos,normal,albedo,r0,gloss,emmissive,bumpNormal, 通过Scene_GetSurfaceInfo(SceneResult)计算获得。SurfaceLighting结构包含diffuse,speclular,Light_Add函数计算pbr光照并返回这个结构,PointLight,SpotLight,DirectionLight三种灯光会计算shadowAttenuation并调用Light_Add,这种写法值得参考。Scene_GetColorAndDepth函数就开始trace并循环bounce累加光照color,其中会计算skyColor,Env_ApplyAtmosphere,以及灯光的glow部分的颜色. bounce使用的是基于bumpNormal的reflection 不同的surface属性都是通过GetSurfaceInfo函数判断ObjectID后计算获得,血splat部分是采的一张随机岩石的贴图,和血的颜色调制而成,最后乘到surface的albedo里并设置相应的gloss和r0。接下来是场景塑形,Tile的边缘bump是通过sqrt(1.-(1.0-fEdge))获得,fEdge是abs(fract))格子的边界计算。南瓜是先在xy平面画sdf,眼睛鼻子是三角形dot(normal)并镜像,嘴巴是两个圆裁剪,最后根据厚度和前后平面相交获得立体sdf,然后南瓜是sin(atan(xz))和圆smin最后在剪裁这个carving部分最终成型,mainImage相机计算并保存jitter用于后面的TAA。BufferB主要是读出旧相机reproject 作TAA,Taa的colorSpace是在Tonemap后作的cliptoAABB。 BufferC是DOF,BufferD是bloom. Image处理一些后效包括colorgrading.这个demo功能完整代码结构合理值得参考

这又是一个water erosion的例子,整个erosion在bufferA完成,Image负责采样bufferA,计算xz方向的slop,cross计算法线进行渲染。BufferA部分,TestOutFlow对一个邻居计算slop返回inout lowestSlope和index. ProcessNeighbour处理25个邻居(其实只处理了8个方向的邻居),计算每个邻居的slop,TestOutFlow八个方向的邻居调用八次,用和自己最小的slope的邻居的slop叠加到inSlop,同时叠加这个邻居的xy到inFlow. 然后mainImage,首先把像素周围5*5的grid共25个邻居(自己在正中间)填入samples数组用于传入ProcessNeighbour,然后调8次(8个邻居)ProcessNeighbour(可以理解为周围8个邻居的周围8个邻居和自己最小的slop用于计算水的流向),获得最小slop和最终叠加的inFlow,然后fragColor.xy+=inFlow*flowRate计算出buffer值用于下一帧迭代。fragColor.w是sediment层积物高度也就是最后的形状,z是speed通过最小slop调制。 Image部分的渲染,循环numSlices,从上到下和numSlices个平面t=height/ray.y获得t计算出交点pos. 只对层积高度高于当前面的数据通过xz两个方向cross出normal. 有了normal就可以计算fresnel,然后插值reflection和water的颜色.ground的颜色根据normal调制出雪的部分,沙,水的部分,最后用tap.x(bufferA.x)在ground和water之间插值,然后是groundfog,用三个exp融合高度和距离算出一个visiblity在1和fragColor之间插值得到最终颜色

这个tardigrade水熊虫的案例非常逼真,塑形部分其实基本都是sdEllipsoid,牙齿部分xy平面atan(xy)镜像,腿画四个abs(x)镜像一下,爪子是三个sdEllipsoid在传入的轴向上旋转不同角度,最后整体在cylinderical空间上采样贴图作一下displacement。RayMarch是经典的rayMarch但返回的z通道填入了march步数用于最后渲染边界,这个做法类似之前的一个voxel pacman的例子。本案例中值得注意的部分是一个NorCurv函数,通过中心偏移类似grad计算的方式模拟出一个curve作为另一种ao调制color,然后lighting函数计算diffuse,spec和一个rimlighting. BackGround函数传入rayDir调制uv采样纹理。mainImage部分raymarch,NorCurv,AO,调制出最终color。BuffBCD都是blur pass.Image部分noise扰动uv视线全屏noise风格,同时叠加之前raymarch的迭代次数用于高亮边缘作为一种显微镜的效果。

本案例有一些亮点值得关注,塑形部分,云分为tornadoShape,CloudShape两部分,tarnadoShape通过传入tornadoNoise作半径,然后sdf一个cylinder通过随y在xz旋转的空间变换p塑形而成。CloudsShape是一个扁的box.注意这里有一些废代码。最后cloudshape裁剪一个球作漩涡,再和tornadoNoise融合而成,tornadoNoise+=cloudnoise+tornadoShape,这里写的很乱,然后再terrainSdf和云组成一个mapSDF函数。GetNoiseDrop通过hash(floor),fract变换uv计算出一个网格的随机偏移位置。然后是渲染部分,首先是box相交返回faceNo和hit,MapBox函数获得face的UV传入GetNoiseDrops调三次调制出rayOffset根据faceNo扰动对应平面的raydir来模拟视线弯折,然后refract(raydir,boxNormal)得到折线,最后计算fresnel,在折线采样背景和reflect反射线采样背景间插值。没有碰撞则直接背景色,这个过程有点绕但非常新奇简略的实现值得参考 然后迭代128次对云mapsdf作渲染,在太阳方向会再调用一次mapsdf调制并累加light, 视线方向exp衰减并累加visibility,但不参与退出条件,退出条件由mapsdf的d决定,这个渲染非常简略值得参考。最后根据不同的object计算lighting。由于之前已经插值出一个bgcolor,后面主要对地形部分diff,spec,reflect着色,以及softshadow。

臭屁虫sdf,基本就是sdElipsoid和roundbox, 脸部用一个p.y+=sin(p.x)调制的空间绘制sdRoundBox和body smin融合,嘴巴是两个sdRoundedCylinder剪裁,眼球p.x=abs(p.x)两个sdElipsoid然后smin body,牙齿两个sdRoundBox融合, 嘴巴top.y+=top.z*top.z让空间随z下垂画sdElipsoid,大虫biglarva的嘴巴是一个sdTriPrism用body剪裁,触须是sdBezier,大石头小石头都是roundbox+sdElipsoid. 渲染部分舌头和眼球加了spec,spec是用pow(dot(reflect(sun,n),rd),28)调制,这里用反射代替H。牙齿的光照col+=dot(rd+sun,n)这里用了h,但没有pow,只是用spec点缀一下, 最后softshadow,ao, 然后render函数外套个AA循环完成渲染

这个冰山的例子虽然没有海面的折射部分但是依然不妨碍是一个表现神作。塑型部分极具亮点参考价值较高,cyclicNoiseCloud,sea,rock,grass,wind,这一系列函数,都是利用同一套sin错叠空间octave次累加noise的turbulent的fbm算法.设定一个球体和大气厚度迭代累积atomosphericDensity获得opticalDepth. 其中atomosphericDensity(p)函数是在大气位置的衰减因子,最终getAtmosphere函数计算:exp(-(opticalDepthSun+opticalDepthView)*scatterCoeff)太阳方向和视线方向的两个opticalDepth得到transmittance,调制累加accumAtmo大气颜色返回.BufferA使用common的塑形函数对cloud,sea,rock塑形。Rock通过abs(p.y)上下对称.map函数会整合sea和rock并赋予id,其中sea会叠加用sin(iceberg.x)调制出围绕冰山的波纹。mainImage负责光追bounce每次获得新的rd,只处理sea的反射和iceburg的折射,iceburg内部side为-1 同时累加accumIceberg. raymarch如果是t>maxDist要区分来自iceberg的refract射线就hitcolor累加sky*atten,如果是来自海面反射对原来的hitcolor根据opticalDepthView衰减并叠一个当前位置出发的atmosphere. 接下来raymarch如果小于marchEps说明碰海面和冰山就计算光照参数,n,h,diff,spec,fres,ao,这里都是float类型,spec用的dot(nh),fresnel=dot(nv),然后根据objectid返回对应的射线,同时计算color累加到hitCol. 其中iceCol=(fres+spec)*skyCol*atten;atten=1.-albedo, water的话只计算一个atten*=(spec+fres)*ao*albedo, atten每次bounce都会参与调制. 这种光照方式比较新奇,atten相当于反射率*albedo这里,而fresnel,spec,diff都用来算atten。 接下来就是大气,云,风的渲染. 其中cloud和wind都迭代数次,cloud会采一个太阳方向的mapCloud,最终累积cloudAccum.然后color=mix(hitcolor,windAccum,windT) 使用风的noiz对颜色进行插值,这是一个亮点值得学习。另外,getSun函数视线了一个flare,参与到atomsphere计算中,效果很棒值得参考,BuffB是一个fxaa的pass,Image acetonemapping

这是一个特别的基于pathtracing的pbr光照模型的例子,common里提供了丰富的旋转函数,复数操作lib,值得参考,本案例的fresnel,thinfilminterference参数计算是基于复数空间(但是没有使用thinfilm模型),common里还包括了bsdf的一些功能函数,BuffA是整个bsdf光追框架,bsdf表面的采样SampleBSDF_smith根据一个SamplePhaseFunction返回incomingDirection(wi),和throughput, SamplePhaseFunction函数会根据一个复数空间的fresnel计算返回throughput和wi,过程中会用fresnel参数计算一个specBounceProbability,用这个概率决定是镜面反射还是漫反射,会返回不同的射线和throughput. sampleRefractBSDF_Smith类似的采样的是折射bsdf,其中会调用SampleDielectricPhas类似的会通过fresnel_T和fresnel_R函数(对应透射和反射fresnel)返回一个概率输出不同的throughput和reflect/refract射线。 sampleInterface通过内外判定调用SampleBsdf_smith/SampleRefractBsdf_smith, SampleBSDF_Layerd是一个总框架根据不同的媒介ior(复数形式)循环调用SampleInterface返回对应的射线和throughput. DoEnviromentShading和球体碰撞获得tbn,计算出tbn空间的raydir,调用SampleBSDF_Layered返回throughtput乘上环境贴图,这个函数相当于碰撞和光照计算的集成,场景中有三个球,就调用三次进行绘制。cube是输出一个环境cubmap,image作acetonemapping。本案例的亮点是pbr光追的科教模型,ior是复数形式,fresnel会在复数空间计算,学习价值较高。

作者和之前cyclicNoise冰山的作者是同一个,不过这次地表和云的塑形没有使用cylicNoise,而是octave的valueNoise ,太阳的flare依旧是原来亮点之一。云的轮廓塑形同之前作品一样,设定地球半径,大气厚度和高度,计算出云底部和厚度,smoothstop并调制云octave的valuNoise。map函数smin陆地land和sea,land和sea都是fbm. 本作的第二大亮点和冰山一样就是atomosphere的渲染部分,先根据地球半径大气厚度计算atmosphericDensity衰减函数,最终函数OpticalDepth(p,rd,len)通过迭代路径上的每一步累加atomosphericDensity获得opticalDepth(可以理解为能见度因子),最后getAtmosphere函数通过计算两个方向的len(SunDir,ViewDir分别到atmosphere的距离)获得对应的opticalDepth,然后exp(-(opticalDepthSun+opticalDepthView)*scatteringCoef)得到transmittance,最后和scatteringCoef(散射颜色比例)调制累加accumAto并返回得到大气颜色. mainImage部分和冰山作品一样直接在rayMarch里计算albedo, 先对陆地和海洋raymarch,然后海洋内部raymarch,hit判定,计算gournd和sea的h,diff,spec,fres,海洋的foam用了个voronoi噪声, 然后cloud的raymarch,云会计算视线和太阳两个方向的mapClouds(sunDir方向调用一次),计算absorption调制并累加cloudAccum,最后叠加sunflare效果

这是个刻画非常细致的营火效果,实现方式有点累,看代码也累虽然注释的清晰。灯光主要由common里的area light包括tubelight,disclight来模拟火焰的光照, 塑形方面木材logs是sdcapsul,每根logs的塑形同时还包括flame,由一系列sdElipsoid通过sincos调制坐标系绘制。sparks部分通过mod镜像空间hash出每个网格的粒子属性绘制sdElipsoid。燃料或岩石是沿着polar空间镜像摆放,岩石本身的塑形是mod镜像空间用不同的sphere作displacement而成。volumetric的inscatterTransmittance是直接在raymarch里做,主要是blackbody黑体颜色和step距离衰减调制出transmittance的color, shadow部分fire也有shadow. raymarch都会用到cone参数优化步数, 所有的sdf都会计算uv,地表会做displacement。作为一团火,效果着为细致。最后mainImage里还加上了TAA. Image里是bloom pass

主要是塑形部分,树岩石树干都是noise做octave然后叠加到p做扰动并绘制sdSphere., 计算softshadow,ao,diffuse,spec, fog. 根据不同材质采样纹理

这是一个神奇的例子,BuffA是球的物理运算,计算速度,重力,并对位置积分,其中碰撞部分使用了normal(p,otherp),map(p,otherp),用每个球的中心位置去计算normal和mapsdf,获得距离和法线就可以用来处理碰撞的repulsion. buffA保存了5个球的位置和速度用于每帧迭代。 Image负责渲染,渲染有自己的map和normal, map函数做55双层循环遍历球体(采buffA),对每个球体去剪裁smax掉其他球体的相交部分在和自己的sdf插值获得一个圆润的效果。最后min融合每一个做过裁剪的球体,同时在第一层循环中填入当前处理的球体的id.最后球体还要smax裁剪一下box。 shade部分采用了pbr,参考的unreal的pdf已经收录,代码写的很简洁值得参考。加入了ao,shadow, 球体的raymarch及shading后,再射线ibox做碰撞用来绘制box. 值得注意的是box的边界,pos=ro+brd*ib2.y,e=smoothstep(brad-0.15,brad,abs(pos))计算获得,最后box的color叠上一个triNoise.

这个例子非常棒的宇宙星球渲染范例。亮点较多,人物的塑形并不复杂,由通过sincos调制sdbox的长宽高,sdcapsule,sdellepsiod等根据时间序列绘制。在人物的飞行序列动画段中除了固定的星球还会在路径上加入随机星球,星球的渲染包括ring先塑形,然后计算float的diff,spec,等光照参数,最后着色星球包括地表水火山大气等,分成了这三部分都在addPlanet中调用。值得注意的是background和starfield的实现,starfield是一个cylinder坐标系的uv打格子,然会使用chromatic aberration对三个通道分别调制出红蓝位移效果的粒子。background(rd)是一个无限循环分形采样的范例,几句话概括的话,一个循环按alpha叠加很多层纹理,每层采样的坐标系按照时间序列变换,同时alpha也是时间序列的blend因子,用于时间片首尾的blend。整个范例书写灵活易懂有较高学习参考价值

这是一个神经网络训练的volume模型,输出是vec4(density,normal),共有3个隐藏层,前两个用sine激活函数,第三个隐藏层用tanh,输出使用sigmoid激活函数,使用adam优化器在单线程cpu训练15分钟(具体不知道机器配置)跑了100-200个epoches,其中batchsize为500,使用平方差损失函数,训练出275个weights。本作品的神经网络forward部分可以帮助理解深度神经网络的原理,就是对每一个输入特征做线性变换的过程,激活函数用于在层间引入非线性因子,可以理解为转换为概率(当前特征来自前一层的所有特征的线性组合而成的概率密度函数),通过梯度下降计算损失函数的梯度获得修改权重不断调整线性变换的w,b,最终损失函数导数为0使得我们的拟合函数和目标函数一致,梯度停止反向传播达到收敛。在这里输入特征是3d坐标,作者通过大量3d 点云模型和boundingbox的空间配对训练出的权重,lossy应该就是方差平均。这只是猜测没去细究 渲染部分是一个transfer函数,通过输出的通道x(density),yzw(normal),计算光照,在raymarch函数里使用体积渲染的方式exp衰减并累加absorb,然后在视线方向调制累加最终颜色值,其中为了避免层纹,根据ro+rd转换成jitter扰动量扰动初始t。

这个作品演示的是空间镜像,背景的小方块螺旋组合是mod(floor(p/interval)),再p.xy=rot(p.z),再mod(atan(p))这样的空间镜像而成,整个过程再函数dist1里,由于是用了interval,把这个粒度用sdbox(p)绘制出来就是一个方块集合, dist2函数是中间的魔方小方块的镜像+绘制,两个abs和缩放叠加,最后sdbox。值得一提的亮点是本作的edge处理是通过两个不同eps的calcNormal(p,eps)距离调制而成.

这是一个模拟叶片下落的渲染demo,原理是球体上绘制心形纹理,raySphere射线和球体碰撞检测,GetSphereUVs获得rd方向的球面前后两层纹理用到了四元素去计算,heartBall用这两层纹理计算backMask和frontMask,也就是心的图案,然后diff光照后在两个颜色之间用frontMask插值。值得注意的是blur是通过球体前后的坐标位置决定的,传入heart纹理绘制函数,产生模拟dof的效果。然后有一个边界计算的算法,通过len(cross(p,rd))获得边界浓度,其中p是球体的位置

这是一个简洁的实时全局渲染的例子,由于并没有对颜色累加做蒙特卡洛pathtracing,只是基本的半球面随机漫反射射线的光线追踪,平面部分使用了abs(fract)格子绘制,在sdf计算的同时获得对应的emision,然后迭代36次,根据法线计算半球面射线计算新的射线,累加emision。平均后输出。其中会有一个简单的通过扰动rd的dof效果, 这个动态框架非常适合做playground。

和之前的那个实时全局渲染的例子同一个作者同一个框架,不同的是加入了折射,由于都是存反射折射,因此没有加入半球面随机法线,所有的射线都是确定的,没有噪点,背景用了一个sky包装一个格子sdf,折射部分用到了ior计算refract,每次射线迭代会累积prod,透射情况prod*=depth;prod*=1.-fre,反射情况prod*=pow(fre),其中depth是累积射线步,因此可以把prod看作是一个带有absorb和反射率共同作用的衰减因子。这个作品也同样适合做playground, 有一个值得注意的亮点,主体sdf函数中使用了空间镜像和球体之间做了一个len(vec2(d,spheresdf))的操作,其中的d是镜像坐标系中的最小值。这个操作基本等价于max(d,spheresdf)。

这是一个简单出众的啤酒效果,泡沫,啤酒,都有根据sin(uv)调制的形状mask,以及对应的color计算,bubbleMask会在y轴上使用一个镜像复制mod(abs)实现很多泡泡的效果。由于用了循环绘制泡泡,如果增加泡泡数量性能会有所影响

本作的氛围感很强,着重点主要在塑形上,房屋主要是屋顶和镜像空间pModInterval出的窗户,场景sdf中会镜像空间绘制building,桥洞使用pMod1计算,最后和河流smin, sdf同时会返回材质。 渲染部分在水面部分会按反射方向再调用一次raymarch,水面normal通过triplanarNormal这个函数以及sin调制而成,值得注意的是triplanaNormal这个函数是一个bumpmap计算,需要调用三次普通的triplana采样获得不同方向delta下的高度值来计算梯度。 BuffB有一个简单的模糊AA,绘制日夜循环的背景氛围,最后aces tonemapping输出。Image部分实现了bloom效果。

这也是一个非常不错的光追框架,玻璃瓶和酒杯使用了sdtorus并且在半径参数上用了由smoothstep组合而成的curve(h)。球体是碰撞检测,通过intersect函数把酒杯和酒瓶sdf和球体的碰撞检测融合在一起。返回材质id和反射类型。render函数就是执行12个bounces反弹,对ROUGH,DIELECTRIC两种反射类型进行分别处理,ROUGH部分根据fresnel计算反射概率然后在全反射射线和漫反射射线之间通过ior进行插值。对于DIELECTRIC,则根据ior计算refract ray, 通过fresnel和ior计算反射概率,大于反射概率就返回折射线否则反射线。Image部分通过扰动ro实现dof,并迭代采样bufferA自身累积颜色实现蒙特卡洛pathtracing。image叠加了个aces和vignette,当然还有gamma矫正。

亮点是尾部的推进火焰,颜色使用了blackbody,塑形是fbm+sdcone, 尾焰还有normal. 船身使用sdEllipsoid和sdCapsule以及窗户合并合成,窗户是abs(p.x)绘制镜像的sdRoundBox,引擎部分是两个sdCone裁剪,护栏是sdTorus和镜像的四个sdCappedCone,背景部分使用了triplanar采样贴图叠加一个voronoi绘制的star, 光追的时候额外对尾焰做碰撞并shade尾焰,shadeExhaust函数中通过exhaustNois计算temperature来计算blackbody.exhaustNoise就是个fbm(p+vec3(0,time,0))+cone,此外exhaustNormal用来计算rim作为一个thickness因子。

这是一个spheretracing的例子,区别于传统的raymarching, spheretracing会调制t计算出一个阈值半径sphereR,d=map(p),d小于sphereR时计算并累加normal,退出条件是alpha=(1.-alpha)*((sphereR-d)/sphereR),alpha>0.8. 这种tracing方式可以同时解决了dof效果,fog效果。另外本作的glare效果值得参考。