AlgebraMaster

Modern C++ 创造非凡 . 改变世界

导航

VIS RAYTRACING 2

 1, Triangle SDF

 

 

发现Houdini行列式竟然会出问题????有人测试过matrix2吗。。。。。

float area_tri(vector2 a, b ,c){
    float m11 = b.x - a.x;
    float m12 = c.x - a.x;
    float m21 = b.y - a.y;
    float m22 = c.y - a.y;
    
    //  THIS WILL CAUSE ERROR -----
    //matrix2 m = set( m11,m12,m21,m22) ;
    //float area = determinant(m);
    //return area * 0.5f;
    //  THIS WILL CAUSE ERROR ------
    
    
    return 0.5f*(a.x*b.y + b.x*c.y + c.x*a.y  -a.x*c.y - b.x*a.y - c.x*b.y);
}

// 

int in_tri(float abs_AF,abs_BT,abs_GM)
{

    if (abs_AF > 0.99999f) return 0; 
    if (abs_BT > 0.99999f) return 0; 
    if (abs_GM > 0.99999f) return 0; 
    if( abs_AF + abs_BT +abs_GM> 1.0f) return 0;
    return 1;
}

// define our triangle
vector2 a = set(-2.0f, 0.0f);
vector2 b = set(2.0f, 0.0f);
vector2 c = set(0.0f, 2.0f);

vector2 inp = set(@P.x, @P.y);
    
float A = area_tri(a, b, c);

float Aa = area_tri(inp, b,c);
float Ab = area_tri(inp, a,c);
float Ac = area_tri(inp, a,b);

float AF =  Aa / A;
float BT  = Ab / A;
float GM =  Ac / A;


float abs_AF = abs(AF);
float abs_BT = abs(BT);
float abs_GM = abs(GM);
f@sdf = 0;
if (in_tri(abs_AF,abs_BT,abs_GM) ) // if exist in tri;
{
    f@sdf =  min(abs_GM, min(abs_AF,abs_BT) );
}
else{
    f@sdf = 0 ;
}

 

2,map 2d points to sphere:

假如一些随机点应用:

phi = u * phi_max
theta= theta_min  + v* (theta_max- theta_min) 

x = r sin(theta) cos(phi)
y = r sin(theta) sub(phi)
z = r cos(theta)

 

float r = chf("radius");
float u = chf("u");
float v = chf("v");

float phi_max = rand(@ptnum*10058) * 2.0f * PI;

float theta_min = rand(@ptnum*1000);
float theta_max = rand(@ptnum*500) * PI;


float phi =  u * phi_max;
float theta = theta_min + v*( theta_max - theta_min);

float x = r * sin(theta) * cos(phi);
float y = r * sin(theta) * sin(phi);
float z = r * cos(theta);


@P.x = x;
@P.y = y;
@P.z = z;
View Code

 

This form is particularly useful for texture mapping, where it can be directly used to map a texture defined over[0,1]^2 to the sphere

u = 0.0 v=1.0

 

u=0.5 v=1

u=v=1

 

均匀分布在全球:

参考https://github.com/diharaw/GPUPathTracer/blob/master/src/shader/path_tracer_cs.glsl

vector random_in_unit_sphere(int state)
{
    float z = rand(state*131) * 2.0f - 1.0f;
    float t = rand(state*12) * 2.0f * 3.1415926f;
    float r = sqrt(max(0.0, 1.0f - z * z));
    float x = r * cos(t);
    float y = r * sin(t);
    vector res = set(x, y, z);
    return res;
}


for(int i=0;i<100000;i++)
{
    addpoint(geoself(),random_in_unit_sphere(i) ) ;

}
View Code

分布很均匀。

方法2:

 

vector random_unit_vector(int i) {
    float a = rand(i*502) * 2 * PI;
    float z = fit(rand(i*50000),0,1,-1,1);
    float r = sqrt(1 - z*z);
    return set(r*cos(a), r*sin(a), z);
}

for(int i=0 ; i< 10000;i++){

    addpoint(geoself(),random_unit_vector(i));
}
View Code

 

 

 

用球坐标分布在球内:

vector random_in_unit_sphere(int state)
{
    float u = rand(state);
    float v = rand(state+56465);
    float theta = u * 2.0 * PI;
    float phi = acos(2.0 * v - 1.0);
    float r = cbrt(rand(state+561) );
    float sinTheta = sin(theta);
    float cosTheta = cos(theta);
    float sinPhi = sin(phi);
    float cosPhi = cos(phi);
    float x = r * sinPhi * cosTheta;
    float y = r * sinPhi * sinTheta;
    float z = r * cosPhi;
    return set( x, y, z);
}


for(int i=0;i<10000;i++)
{
    addpoint(geoself(),random_in_unit_sphere(i) ) ;

}
View Code

 

这个球内简直更均匀https://karthikkaranth.me/blog/generating-random-points-in-a-sphere/#better-choice-of-spherical-coordinates

跟这个相关的分布http://corysimon.github.io/articles/uniformdistn-on-sphere/

http://datagenetics.com/blog/january32020/index.html

https://www.jasondavies.com/maps/random-points/

 

Raytracing in one weekend用球坐标分布在球内:

 

 

 

vector random_in_unit_sphere(int seed) {
    while (true) {
        float vx = rand((seed + 1 ) * 2);
        float vy = rand(seed + 320);
        float vz = rand(seed + 1744);
        
        vx = fit(vx, 0,1, -1,1);
        vy = fit(vy, 0,1, -1,1);
        vz = fit(vz, 0,1, -1,1);
        
        vector v = set(vx,vy,vz);
        
        if (length(v) >=1 ) break;  // 按道理说这里应该是continue,不知道houdini应该处理不了?
        else return v;
    }
    return 0;
}


for(int i =0;i<10000;i++){
    vector p = random_in_unit_sphere(i*20);
    addpoint(geoself(), p);
}

 但是经过我测试,for循环竟然完美支持continue:

vector random_in_unit_sphere(int seed) {
    for(int i=0;i<1000;i++){
        float vx = rand((seed + 1 ) * 2);
        float vy = rand(seed + 320);
        float vz = rand(seed + 1744);
        
        vx = fit(vx, 0,1, -1,1);
        vy = fit(vy, 0,1, -1,1);
        vz = fit(vz, 0,1, -1,1);
        
        vector v = set(vx,vy,vz);
        
        if (length(v) >=1 ) continue;
        else return v;
    }
    return 0;
}


for(int i =0;i<10000;i++){
    vector p = random_in_unit_sphere(i*20);
    addpoint(geoself(), p);
}

 

True lambertian里面还说了一些:

稍微对这个方法修改,就可以达到球表面分布:

https://raytracing.github.io/books/RayTracingInOneWeekend.html

Picking random points on the unit sphere can be achieved by picking random points in the unit sphere, and then normalizing those.

vector random_in_unit_sphere(int seed) {
    for(int i=0;i<1000;i++){
        float vx = rand((seed + 1 ) * 2);
        float vy = rand(seed + 320);
        float vz = rand(seed + 1744);
        
        vx = fit(vx, 0,1, -1,1);
        vy = fit(vy, 0,1, -1,1);
        vz = fit(vz, 0,1, -1,1);
        
        vector v = set(vx,vy,vz);
        
        if (length(v) >=1 ) continue;
        else return v;
    }
    return 0;
}


for(int i =0;i<10000;i++){
    vector p = random_in_unit_sphere(i*20);
    addpoint(geoself(), normalize(p) );  // Normalize就行
}

 

 

误区修复,我突然发现houdini的seed的问题,不是while循环的问题:

xy平面的disk撒点。houdini rand依赖seed所以关键问题必须给seed做一份copy,然后让rand_range()函数种子每次都不能一样。这个是关键。!

 

 

float rand_range(int seed; float low; float high){
    return fit(rand(seed), 0,1, low, high);
}

vector random_in_unit_disk(int seed) {
    int seed_copy = seed+13727;
    while(1)
    {
        vector p = set(rand_range(seed_copy, -1.0,1.0), rand_range(seed_copy + 100, -1,1), 0);
        if (length(p) >= 1) {
            seed_copy+=7;
            seed_copy*=2;
            continue;
        }
        else
            return p;
            
    }
}



for(int i=0;i<80;i++){
    vector v = random_in_unit_disk(i);
    addpoint(geoself(), v);
}

 

 

 

 

 

 

3:填充的半球点 openglSSAO一章提到:

float llerp(float a;float b;float f)
{
    return a + f * (b - a);
}

int nsamples = 14164;
for (int i = 0; i < nsamples; ++i)
{
    
        vector sample = set( rand(i)* 2.0 - 1.0,
        rand(i*51400) * 2.0 - 1.0, 
        rand(i*5024713));
    
    sample = normalize(sample);
    sample *= rand(i*50723);
    float scale = float(i) / float(nsamples); 
    scale = lerp(0.1f, 1.0f, scale * scale);
    sample *= scale;
    addpoint(geoself(),sample);
    
    
}
View Code

 

4,Sphere mapping:

开局给你一个球,怎么贴HDR?实际就是求UV

 

 

float u = atan(@P.z , @P.x);
u/= (2 * PI);
float v = acos(@P.y);
v/= PI;
v = 1- v;
string tex = chs("texture");
@Cd = texture(tex, u ,v);

开局给你一个向量,这个向量为任何向量:

float phi = atan2(dir.x, dir.z);
float theta = acos(dir.y);
float u = phi / (2.000 *d_pi );
float v = 1 - theta / d_pi;
//u = clamp(u,0,1);
//v = clamp(v,0,1);
vector imgL = texture(ch("img"),u,v);

 

5, sample Cube map

string img = chs("img");
vector2 SampleSphericalMap(vector v)
{
    vector2 invAtan = set(0.1591, 0.3183);
    vector2 uv = set(atan(v.z, v.x), asin(v.y));
    uv *= invAtan;
    uv += 0.5;
    return uv;
}
vector dir = normalize(@P);
vector2 uv = SampleSphericalMap(dir);
vector color  = texture(img, uv.x, uv.y);
@uv = uv;



@Cd = color;
View Code

 

6, 球坐标与直角转换

只要注意你画半圆:

 

 

for(int i=0 ;i < 10000; i++)

{
    float theta = rand(i+4573) * (PI/2.0f);
    float phi = rand(i*211020) * (2.0f* PI);
    
    float x=sin(theta)*cos(phi);
    float y=sin(theta)*sin(phi);
    
    float z=cos(theta);
    
    addpoint(geoself(), set(x,y,z));
}

只要看你得theta的 区间是PI/2 ,所以 半圆在Z轴。一般都放到z轴,然后要把变换到法线位置(一下是线性变换,矩阵也可以)

// spherical to cartesian (in tangent space)
 vec3 tangentSample = vec3(sin(theta) * cos(phi),  sin(theta) * sin(phi), cos(theta));
// tangent space to world
vec3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * N; 

 

7,ImportanceSampleGGX && "CosWeightedSampleGGX" && Align-to-normal-method

两者都是从[0-1] [0-1]的范围采样变成半球。

ImportanceSampleGGX:

带有roughtness的参数.注意下面的代码roughness 是平方的,disney和epic的区别就是epic平方了

float VanDerCorpus(int n; int base)
{
    float invBase = 1.0f / float(base);
    float denom   = 1.0f;
    float result  = 0.0f;

    for(int i = 0; i < 32; ++i)
    {
        if(n > 0)
        {
            denom   = float(n) % 2.0f;
            result += denom * invBase;
            invBase = invBase / 2.0f;
            n       = int(float(n) / 2.0f);
        }
    }

    return result;
}
// ----------------------------------------------------------------------------




// ----------------------------------------------------------------------------
vector2 HammersleyNoBitOps(int i; int N)
{
    return set(float(i)/float(N), VanDerCorpus(i, 2));
}




vector ImportanceSampleGGX(vector2 Xi;vector N;float roughness)
{
    float a = roughness*roughness;

    float phi = 2.0 * PI * Xi.x;
    float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y));
    float sinTheta = sqrt(1.0 - cosTheta*cosTheta);

    // from spherical coordinates to cartesian coordinates
    vector H;
    H.x = cos(phi) * sinTheta;
    H.y = sin(phi) * sinTheta;
    H.z = cosTheta;

    // from tangent-space vector to world-space sample vector
    vector up        = abs(N.z) < 0.999 ? set(0.0, 0.0, 1.0) : set(1.0, 0.0, 0.0);
    vector tangent   = normalize(cross(up, N));
    vector bitangent = cross(N, tangent);

    vector sampleVec = tangent * H.x + bitangent * H.y + N * H.z;
    return normalize(sampleVec);
}





int n = 4096;
for(int i=0;i<n;i++){
    int test = i;
    vector2 xy = HammersleyNoBitOps(test,n);
    vector testNormal = chv("normal");
    vector newpos = ImportanceSampleGGX(xy,testNormal, chf("roughtness"));
    addpoint(geoself(),newpos);
}
View Code

CosWeightedSampleGGX:

带个指数级衰减:

vector2 genSamplers(int i){
    float sx = i;
    float sy = i*100;
    return set(rand(sx),rand(sy));
}

vector CosWeightedSampled(vector2 Xi;vector N;float e){
    float cos_phi = cos(2.0f * PI * Xi.x); 
    float sin_phi = sin(2.0f * PI * Xi.x); 
    
    float cos_theta = pow((1.0f - Xi.y) , 1.0f / (e+1.0));
    float sin_theta = sqrt(1.0f - cos_theta * cos_theta);
    
    // Generation Z based sphere
    float pu = sin_theta * cos_phi;
    float pv = sin_theta * sin_phi;
    float pw = cos_theta;
    vector H = set(pu,pv,pw);
    
    
    // from tangent-space vector to world-space sample vector
    vector up        = abs(N.z) < 0.999 ? set(0.0, 0.0, 1.0) : set(1.0, 0.0, 0.0);
    vector tangent   = normalize(cross(up, N));
    vector bitangent = cross(N, tangent);

    
    vector sampleVec = tangent * H.x + bitangent * H.y + N * H.z;
    return sampleVec;
}


int n = 4096;
for(int i=0;i<n;i++){
    int test = i;
    vector2 xy = genSamplers(test);
    vector N = chv("normal");
    float e = chf("e");
    vector sampleVec = CosWeightedSampled(xy,N,e);
    addpoint(geoself(), sampleVec );
}
View Code

 

 

 

这里带入一种直接对齐方法,原理简单,直接对其到N方向上的半球,这种方法比上面的简单的多,输入方向N:

vector random_unit_vector(int i) {
    float a = rand(i*502) * 2 * PI;
    float z = fit(rand(i*50000),0,1,-1,1);
    float r = sqrt(1 - z*z);
    return set(r*cos(a), r*sin(a), z);
}

vector N = chv("N");
N = normalize(N);
for(int i=0 ; i< 10000;i++){
    vector dir = random_unit_vector(i);
    if(dot(dir, N) < 0){
        dir = -dir;
    }
    addpoint(geoself(),dir);
}
View Code

 

所以raytracing in one weekend 里的半球diffuse也可以这么写:

 

 

8, From RayTracing from ground up

Review the Regular Sampling in a pixel:

 

 

注意这种采样是在一个像素里生成5 * 5 个采样。实际上这种方法问题请大的:

 

Random Sampling:

只用把4.1里的内部代码替换下即可:

 

 

Jittered Sampling: 一个像素25个采样点:

 

 

修改4.1的代码就行: replace the expressions q + 0.5 and p+0.5 to -> q + rand_float() and p+ rand_float()

有时间一定要实现一下:

https://graphics.pixar.com/library/MultiJitteredSampling/paper.pdf

 

随机

 1, 有全局seed的 random[0-1]

static unsigned int seed = 0x13371337;

static inline float random_float()
{
    float res;
    unsigned int tmp;
    seed *= 16807;
    tmp = seed ^ (seed >> 4) ^ (seed << 15);
    *((unsigned int *) &res) = (tmp >> 9) | 0x3F800000;
    return (res - 1.0f);
}

int main()
{
    for(int i=0;i<100;i++){
        cout << random_float() << endl;
    }
}
C++

 

 

参考:fundamentals of computer graphics 

http://learnopengl.com/#!Advanced-Lighting/SSAO

PBRT2

Raytracing from ground up

posted on 2019-10-22 01:19  gearslogy  阅读(401)  评论(0编辑  收藏  举报