Clouds Rendering

Date:     Updated:

카테고리:

태그:

홍정모님의 그래픽스 새싹코스 강의를 듣고 정리한 내용입니다.

Other References:
How Big Budget AAA Games Render Clouds - SimonDev

Fog vs Clouds

Fog Beer’s Law Cloud
205537 203320 205607

이전 포스팅에서 배운 Fog Effect의 경우, 거리에 따라 밝기를 감쇠시킴으로서 구현할 수 있었다. 하지만 Cloud 혹은 Cloud-like object는 같은 방식으로 구현할 수 없다. Boundary가 존재하고, 내부 밀도가 일정하지 않으며, position에 따른 lighting 계산도 해야하기 때문이다. 이번 예제에서는 Object의 Density와 Lighting 정보를 따로 계산해 3D-Texture에 저장해두고 사용할 것이다. 이러한 방식을 이용하면 여러가지 장점이 있다. 먼저 pixel shader에서 interpolation 연산을 진행할 때 중복 연산을 피할 수 있다. 또한 실제 object의 크기보다 작은 texture를 이용해 최적화 할 수 있다는 것도 큰 장점이다.


Density Map

Cloud Absorption
203307 204336
void main(uint3 dtID : SV_DispatchThreadID)
{
    uint width, height, depth;
    densityTex.GetDimensions(width, height, depth);
    
    float3 uvw = dtID / float3(width, height, depth) + uvwOffset;

    densityTex[dtID] = cloudDensity(uvw);
}
  • Perlin-Worley noise를 이용해 Density Map 생성
  • Absorption(감쇠) 연산은 object를 step으로 쪼갠 뒤, (Beer’s law * step size) 적용
  • uvwOffset을 이용해 구름이 흘러가는 연출 구현


Lighting Map

Before After
213704 213749
  • Density Map으로만 구름을 렌더링하면 빛에 반응하지 않음
  • 실제 구름은 해당 위치의 밀도, 빛의 방향 등 여러가지 요소가 결합되어 있음
    • 심지어 구름 내 그림자 영역도 존재


204348

float LightRay(float3 posModel, float3 lightDir)
{
    int numSteps = 128 / 4;
    float stepSize = 2.0 / float(numSteps);

    float alpha = 1.0;

    [loop]
    for (int i = 0; i < numSteps; i++)
    {
        float prevAlpha = alpha;
        float density = densityTex.SampleLevel(linearClampSampler, GetUVW(posModel), 0).r;
        
        if (density > 1e-3)
            alpha *= BeerLambert(lightAbsorptionCoeff * density, stepSize);

        posModel += lightDir * stepSize;

        if (abs(posModel.x) > 1 || abs(posModel.y) > 1 || abs(posModel.z) > 1)
            break;
        
        if (alpha < 1e-3)
            break;
    }
    
  
    return alpha;
}
  • Lighting Map역시 sampling 후 step size로 계산
  • stepSize가 2/numSteps인 이유는 Model의 크기가 [-1, 1] 이기 때문
  • alpha = 1.0 부터 시작. Density Texture에서 density값 가져와 alpha 감쇠
  • 범위를 벗어나거나, alpha가 0으로 떨어지면 break.


Volumetric Render

Scattering

Isotropic Anisotropic
203338 203348
   
float HenyeyGreensteinPhase(in float3 L, in float3 V, in float aniso)
{
    // V: eye - pos 
    // L: Light Direction
    
    float cosT = dot(L, -V);
    float g = aniso;
    return (1.0 - g * g) / (4.0 * PI * pow(abs(1.0 + g * g - 2.0 * g * cosT), 3.0 / 2.0));
}
  • 빛이 구름 내부로 들어오면 내부 분자들에 의해 빛이 산란됨
  • 일반적으로 산란은 모든 방향으로 균등하게 산란하지만, 구름의 경우 빛의 방향에 따라 산란 정도가 다른 모델을 사용함
  • HenyeyGreenstein 모델 사용


float4 main(PixelShaderInput input) : SV_TARGET
{
    float3 eyeModel = mul(float4(eyeWorld, 1), worldInv).xyz; // 월드->모델 역변환
    float3 dirModel = normalize(input.posModel - eyeModel);
    
    int numSteps = 128;
    float stepSize = 2.0 / float(numSteps);
    float3 volumeAlbedo = float3(1, 1, 1);
    
    float4 color = float4(0, 0, 0, 1);
    float3 posModel = input.posModel + dirModel * 1e-6;

    
    [loop]
    for (int i = 0; i < numSteps; i++)
    {
        float3 uvw = GetUVW(posModel);
        float density = densityTex.SampleLevel(linearClampSampler, uvw, 0).r;
        float lighting = lightingTex.SampleLevel(linearClampSampler, uvw, 0).r;

        if (density.r > 1e-3)
        {
            float prevAlpha = color.a;
            color.a *= BeerLambert(densityAbsorption * density.r, stepSize);
            float absorptionFromMarch = prevAlpha - color.a;
            
            color.rgb += absorptionFromMarch * volumeAlbedo * lightColor
                         * density * lighting
                         * HenyeyGreensteinPhase(lightDir, dirModel, aniso);
        }
        
        posModel += dirModel * stepSize;
        
        if (abs(posModel.x) > 1 || abs(posModel.y) > 1 || abs(posModel.z) > 1)
            break;
        
        if (color.a < 1e-3)
            break;

    }

    color = saturate(color);
    color.a = 1.0 - color.a;
    
    return color;
}
  • Density, Lighting 값을 가져온 후 HenyeyGreenstein함수를 이용해 rgb 계산
  • 구름 입장에서 alpha값은 visabilty로 사용
  • 마지막에 alpha값을 반전시켜야 다른 오브젝트(ex. skybox)와 정상적인 블렌딩 가능


Result

HongLabGraphicsExample2024-11-1919-48-03-ezgif com-crop




맨 위로 이동하기

Graphics 카테고리 내 다른 글 보러가기

댓글 남기기