Chango的數學Shader世界(十七)RayTrace三維分形(二)—— Julia Set造型

目的:

移植Inigo Quilez的可視化三維Julia的算法到ue4。

本篇僅考慮造型,忽略表面法線計算和光照模型等。

忽略分形相關細節數學原理。

 

參考:

最近牆越來越高,我也沒法放鏈接了。

1.Inigo Quilez在ShaderToy上的Julia,網址後綴MsfGRr

2.維度:數學漫步第一季的複數(下)

3.Ray Tracing Quaternion Julia Sets on the GPU——Keenan Crane,University of Illinois at Urbana-Champaign

pdf,非論文。

 

觀察:

我們意識到難點有二:

1.完整的JuliaSet沒有解析式,只有遞推式。

若要判斷某一點是否可由初始集合經過k次迭代能被包含,消耗也是巨大而不現實的。

因此我們似乎既沒辦法直接RayTrace,也沒辦法RayMarch出一個Julia雲。

(下面是a=0時的2D情況,演示了2次迭代,這個特殊情況我們能判斷點是否在Julia內,因爲Julia此時爲unit disk)

2.標準Julia集極其小,看不清。

由於是z^2+a,“可見部分”就跟單位圓差不多大小。我們希望搞一個actor,能隨意縮放,位移Julia。但又不改變Julia的那些公式(因爲理論公式現成,要改麻煩)。

 

分析:

1.問題一:根據距離場RayMarch到Julia表面

簡而言之,雖無Julia表面解析式,但有其距離場估計的表達式。

距離場是一個數值場S=F(x,y,z),空間中每點數值爲距離Julia表面最近的距離。

僞代碼

bool intersectJulia(...)
{
    float h = 1.0;
    float t = 0.0;
    for( int i=0; i<最大March次數; i++ )
    {
        if( h<0.0001(碰到表面) ||
        t>最遠距離 (沒有碰到表面) )
        {
            break;
        }
        h = MarchStep( ro+rd*t, ...);
        t += h;
    }
    if( t<最遠距離) { return true; }

    return false;
}
        

2.問題二:移動ro位置

Julia所有表達式不變,對傳入的視線射線的端點位置調整。

ro本來是相機位置camPos。

縮放:對camPos縮放,越小,說明我們越接近單位圓去ray這個Julia,Julia越大。
移動:camPos-JuliaPos

 

步驟

1.計算ro

注意到RayMarchFunc也被連了進去。它以*1的形式參與ro的計算,而主函數以ro爲參,保證了其usf內的函數先於主函數編譯。

//RayMarchFunc.usf
    return 1;
}

float4 qsqr( float4 a ) // square a quaterion
{
    return float4( a.x*a.x - a.y*a.y - a.z*a.z - a.w*a.w,
                 2.0*a.x*a.y,
                 2.0*a.x*a.z,
                 2.0*a.x*a.w );
}

float map(float3 p,float4 c )
{
    float4 z = float4(p,0.0);
    float md2 = 1.0;
    float mz2 = dot(z,z);

    float4 trap = float4(abs(z.xyz),dot(z,z));

    float n = 1.0;
    for( int i=0; i<11; i++ )
    {
        // dz -> 2·z·dz, meaning |dz| -> 2·|z|·|dz|
        // Now we take thr 2.0 out of the loop and do it at the end with an exp2
        md2 *= mz2;
        // z  -> z^2 + c
        z = qsqr(z) + c;  

        trap = min( trap, float4(abs(z.xyz),dot(z,z)) );

        mz2 = dot(z,z);
        if(mz2>4.0) break;
        n += 1.0;
    }
   

    return 0.25*sqrt(mz2/md2)*exp2(-n)*log(mz2);  // d = 0.5·|z|·log|z| / |dz|
}

bool intersectJulia(float3 ro, float3 rd ,float4 c)
{
    float4 res;
    float resT = -1.0;
    float maxd = 10.0;
    float h = 1.0;
    float t = 0.0;
    for( int i=0; i<300; i++ )
    {
        if( h<0.0001||t>maxd ) break;
        h = map( ro+rd*t, c );
        t += h;
    }
    if( t<maxd ) { resT=t;return true; }

    return false;

2.計算rd

和上篇一樣

3.主函數

其中c就是julia遞推式z^2+a的a。

4.結合藍圖Actor

控制材質參數,包括Julia位置等。

5.結果

在ue4默認相機下,正常工作。

我注意到,當我使用ue4影視鏡頭時,由於其FOV並非90度,所以移動起來Julia的位置和大小不正常。這和上面的推導一致。

我還把Inigo Quilez計算法線的部分移植進來。這部分代碼對我來說水平太高且不具普遍性,放棄理解了。先只寫了一個diffuse,看起來不是很好看。對比最近的分形宣傳視頻,果然分形好看還是在光照和材質上。

當然,IQ大神自己是能信手拈來好看的光照模型的,並且作了AA。這是shaderToy原圖:

但他的代碼太過硬核,不適合學習。所以我決定找找其他代碼清晰,而各方面都考慮得比較全面得Shader學習一下。

這是他的光照部分的代碼:

// sky
{
float co = clamp( dot(-rd,nor), 0.0, 1.0 );
vec3 ref = reflect( rd, nor );
//float sha = softshadow( pos+0.0005*nor, ref, 0.001, 4.0, c );
float sha = occ;
sha *= smoothstep( -0.1, 0.1, ref.y );
float fre = 0.1 + 0.9*pow(1.0-co,5.0);
    
col  = mate*0.3*vec3(0.8,0.9,1.0)*(0.6+0.4*nor.y)*occ;
col +=  2.0*0.3*vec3(0.8,0.9,1.0)*(0.6+0.4*nor.y)*sha*fre;
}

// sun
{
const vec3 lig = sun;
float dif = clamp( dot( lig, nor ), 0.0, 1.0 );
float sha = softshadow( pos, lig, 0.001, 64.0, c );
vec3 hal = normalize( -rd+lig );
float co = clamp( dot(hal,lig), 0.0, 1.0 );
float fre = 0.04 + 0.96*pow(1.0-co,5.0);
float spe = pow(clamp(dot(hal,nor), 0.0, 1.0 ), 32.0 );
col += mate*3.5*vec3(1.00,0.90,0.70)*dif*sha;
col +=  7.0*3.5*vec3(1.00,0.90,0.70)*spe*dif*sha*fre;
}

// extra fill
{
const vec3 lig = vec3( -0.707, 0.000, -0.707 );
float dif = clamp(0.5+0.5*dot(lig,nor), 0.0, 1.0 );
col += mate* 1.5*vec3(0.14,0.14,0.14)*dif*occ;
}

// fake SSS
{
float fre = clamp( 1.+dot(rd,nor), 0.0, 1.0 );
col += mate* mate*0.6*fre*fre*(0.2+0.8*occ);
}

 

結語

之後參考比較規整的光照模型,將Julia Set表面整好看點...

1.技術差,就會醜。

2.在非學術的Shader實現裏,我經常看到Inigo Quilez。我最佩服的就是他獨立思考的能力。以參考中的那篇ShaderToy所附帶的技術博客爲例,他拋開常見的z=a+bi+cj+dk形式的複數,自創z=a+bi+cj形式複數,並規定基向量的運算規則,十分大膽。針對實際問題,更改思考框架的操作可以說是我的夢想。

3.在Ray Tracing Quaternion Julia Sets on the GPU中,作者十分風趣,各個細節都十分詳細(就像把你當傻瓜一樣),甚至充滿半頁紙的圖,結尾直接上代碼,你不理解都不行。與滿篇廢話又不着重點,吹上天又沒東西的gp論文比,我太喜歡了。

作者的伊利諾伊大學厄巴納-香檳分校似乎挺牛的。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章