electricsquare/raymarching-워크숍

여 전기 광장

만들어 제시 AJ 주&Huw 보울

개요

렌더링 이미지 결정을 포함합니다 컬러의 모든 픽셀에서 이미지를 필요로하는 무엇을 알아내는 표면 뒤에 속 픽셀에서 세계 고’다음’음영을’그것을 계산하기 위해 최종 색상입니다.

현재 세대 지푸스는 삼각형 메시를 입력으로 사용하여 픽셀로 래스터화한 다음(디스플레이에 그려지기 전에 조각이라고 함)이미지에 대한 기여도를 계산하도록 음영 처리합니다. 이 파이프 라인은 현재 유비쿼터스 있지만,그것은 또한 복잡하고 반드시 그래픽을 배울 수있는 가장 좋은 방법은 아닙니다.

다른 방법은 각 픽셀을 통해 광선을 캐스팅하고 장면의 표면과 교차시킨 다음 음영을 계산하는 것입니다.

‘디스트릭트’를 통한 레이캐스팅 기법을 소개합니다. 디스턴스 필드는 주어진 점이 장면에서 가장 가까운 서페이스에 얼마나 가까운지를 반환하는 함수입니다. 이 거리는 각 점 주변의 빈 공간 구의 반경을 정의합니다. 쿼리된 위치가 지표면’내부’인 경우 그 거리는 음수로 보고되고,그렇지 않으면 양수로 보고됩니다.

레이 행진으로 가능한 것은 무엇입니까?

게임’클레이북’은 디스턴스 필드만을 사용하여 장면을 표현합니다. 이것은 완전히 동적 인 표면 토폴로지 및 모양 모핑과 같은 많은 흥미로운 가능성을 제공합니다. 이러한 효과는 삼각형 메쉬로 달성하기가 매우 어렵습니다. 다른 이점으로는 구현하기 쉽고 고품질의 부드러운 그림자 및 주변 폐색이 있습니다.

https://www.claybookgame.com/

다음 이미지는 또한 우리가 오늘 다룰 것이다 기술을 사용하여 실시간으로 렌더링 된(플러스 우리가 잠수 할 시간이 없습니다 많은 멋진 기술).

당신은 여기에 귀하의 브라우저에서 라이브 실행할 수 있습니다: https://www.shadertoy.com/view/ld3Gz2

자위대(부호있는 디스턴스 필드)를 사용하여 이 장면에 대한 기하학은 마야와 같은 디스턴스 참조에서 생성될 필요가 없었으나,대신 완전히 파라메트리적으로 표현되었다. 이것은 단순히 장면 매핑 기능에 대한 입력을 변경하여 모양을 애니메이션하는 것이 사소한 수 있습니다.

다른 그래픽 효과는 기존의 래스터 화 대안과 비교할 때 레이마칭에 의해 간단하게 만들어집니다. 예를 들어,지하 산란은 단순히 그것이 얼마나 두꺼운 지 확인하기 위해 표면에 몇 개의 여분의 광선을 보내야합니다. 앰비언트 오 클루 전,앤티 앨리어싱 및 피사계 심도는 몇 가지 추가 라인이 필요하지만 이미지 품질을 크게 향상시키는 세 가지 다른 기술입니다.

레이마킹 디스턴스 필드

각 레이를 따라 행진하여 장면의 표면과 교차점을 찾습니다. 이를 수행하는 한 가지 방법은 광선 원점(카메라 평면)에서 시작하여 광선을 따라 균일 한 단계를 수행하여 각 지점에서 거리 필드를 평가하는 것입니다. 장면까지의 거리가 임계 값보다 작을 때,우리는 표면에 부딪혔다는 것을 알고 있으므로 레이마치를 종료하고 해당 픽셀을 음영 처리 할 수 있습니다.

보다 효율적인 접근 방식은 자위대가 반환한 거리를 사용하여 다음 단계 크기를 결정하는 것입니다. 위에서 언급 한 바와 같이,자위대에 의해 반환 된 거리는 입력 지점 주변의 빈 공간 구의 반지름으로 간주 될 수 있습니다. 그러므로 우리는 우리가 어떤 표면도 통과하지 않을 것이라는 것을 알고 있기 때문에 광선을 따라이 양을 밟는 것이 안전합니다.

레이마칭의 다음 2 차원 표현에서 각 원의 중심은 장면이 샘플링된 곳입니다. 그런 다음 광선은 그 거리를 따라 행진(원의 반지름까지 확장)한 다음 다시 샘플링되었습니다.

당신이 볼 수 있듯이,자위대를 샘플링하는 것은 당신에게 당신의 광선의 정확한 교차점을 제공하지 않습니다,오히려 당신이 표면을 통과하지 않고 여행 할 수있는 최소 거리.

일단 이 거리가 특정 임계값 이하이면,레이마크는 픽셀이 교차하는 표면의 특성에 기초하여 음영 처리될 수 있는 종결된다.

여기에 브라우저에서이 쉐이더와 함께 놀러:(클릭하고 광선 방향을 설정하려면 이미지를 드래그) https://www.shadertoy.com/view/lslXD8

레이 트레이싱과의 비교

이 시점에서 우리는 왜 레이 트레이싱이라고하는 기술을 사용하여 분석 수학을 사용하여 장면과의 교차를 직접 계산하지 않는지 물을 수 있습니다. 이것은 오프라인 렌더링이 일반적으로 작동하는 방식입니다.

우리는 대신 거리 필드를 레이마칭합니다.:우리는 명시 적 장면 표현을 작성 할 필요가 없습니다-삼각형 메쉬,텍스 좌표,색상 등

  • 우리는 위에서 언급 한 일부 거리 필드의 유용한 기능의 범위에서 혜택을 누릴 수 있습니다.
  • 위에서 말했듯이,레이 트레이싱에 우아하고 간단한 진입 점이 있습니다. 한 주말 무료 책(및 후속 장)의 레이 트레이싱은 매우 적극 권장되며 그래픽에 관심이있는 모든 사람들에게 필수적인 읽기입니다.

    시작하자!

    쉐이더토이

    쉐이더토이는 쉐이더 탐색,공유 및 토론을 위한 쉐이더 생성 웹 사이트이자 플랫폼입니다.

    계정을 만들지 않고 곧바로 새 셰이더 작성을 시작할 수 있지만 연결 문제가 있거나 무한 루프를 만드는 등의 작업을 쉽게 수행 할 수 있으므로 위험합니다.따라서 여기에서https://www.shadertoy.com/signin로 이동하여 계정을 만들고(빠르고/쉽고/무료)정기적으로 저장하는 것이 좋습니다.

    쉐이더 토이 개요 및 시작 가이드에서는 다음과 같은 자습서를 권장합니다. 여기 기본 워크샵의 나머지 부분을 수행 하는 데 필요한.

    2 차원 자위대 데모

    우리는 2 차원 서명 된 거리 필드를 정의하고 시각화하기위한 간단한 프레임 워크를 제공합니다.

    https://www.shadertoy.com/view/Wsf3Rj

    거리 필드를 정의하기 전에 결과는 완전히 흰색이 될 것입니다. 이 섹션의 목표는 원하는 장면 모양(흰색 윤곽선)을 제공하는 자위대를 디자인하는 것입니다. 코드에서 이 거리는sdf()함수에 의해 계산됩니다. 여기에서 배우는 개념은 3 차원 공간으로 직접 일반화되고 3 차원 장면을 모델링 할 수 있습니다.

    간단한 시작-먼저p점의 엑스 또는 와이 구성 요소를 사용하고 결과를 관찰하십시오:

    float sdf(vec2 p){ return p.y;}

    결과는 다음과 같습니다:

    녹색은’외부’표면을 나타내고 빨간색은’내부’표면을 나타내며 흰색 선은 표면 자체를 묘사하고 내부/외부 영역의 음영은 거리를 나타냅니다. 2 차원에서 이 자위대는y=0에서 2 차원에서 수평선을 모델링한다. 이것이 3 차원에서 어떤 종류의 기하학적 원시적 인 것을 나타낼 것입니까?

    시도해야 할 또 다른 좋은 점은 거리를 사용하는 것입니다(예:return length(p);). 이 연산자는 벡터의 크기를 반환합니다.이 경우에는 원점에 대한 현재 점의 거리를 제공합니다.

    점은 점이 극소이기 때문에 렌더링하는 것은 매우 흥미로운 일이 아니며,우리의 광선은 항상 그것을 놓칠 것입니다!우리는 거리에서 원하는 반지름을 빼서 점을 일부 면적으로 줄 수 있습니다:return length(p) - 0.25;.우리는 또한 크기를 취하기 전에 입력 지점을 수정할 수 있습니다:length(p - vec2(0.0, 0.2)) - 0.25;.이것이 모양에 어떤 영향을 미칩니 까?원 내부의 점에 대해 함수가 어떤 값을 반환 할 수 있습니까?

    축하합니다-방금 수학을 사용하여 원을 모델링했습니다:). 이것은 3 차원으로 확장 될 것입니다.이 경우 구를 모델링합니다. 이 장면 표현을 삼각형 메쉬 또는 누브 표면과 같은 다른’명시적인’장면 표현과 대조하십시오. 우리는 한 줄의 코드로 몇 분 안에 구를 만들었고,우리의 코드는 구에 대한 하나의 수학적 정의,즉’중심점에서 등거리 인 모든 점의 집합’에 직접 매핑됩니다.

    다른 유형의 프리미티브의 경우 거리 함수도 마찬가지로 우아합니다. 아이큐는 이미지와 함께 좋은 참조 페이지를 만든: http://iquilezles.org/www/articles/distfunctions/distfunctions.htm

    프리미티브와의 거리가 어떻게 작동하는지 이해하면-상자에 넣으십시오-매번 코드를 기억하고 쓸 필요가 없도록 함수를 정의하십시오. 셰이더에서 찾을 수 있는 원sdCircle()에 대해 이미 정의된 함수가 있습니다. 원하는 프리미티브를 추가합니다.

    모양 결합

    이제 개별 프리미티브를 만드는 방법을 알고 어떻게 결합하여 여러 모양을 가진 장면을 정의 할 수 있습니까?

    이를 수행하는 한 가지 방법은’합집합’연산자입니다.이 연산자는 최소 두 거리로 정의됩니다. 이 문제를 잘 파악하기 위해 코드를 실험하는 것이 가장 좋습니다.하지만 직감은 자위대가 가장 가까운 표면까지의 거리를 제공하고 장면에 여러 개의 물체가 있으면 가장 가까운 물체까지의 거리를 원합니다.

    코드에서 다음과 같이 보일 수 있습니다:

    float sdf(vec2 p){ float d = 1000.0; d = min(d, sdCircle(p, vec2(-0.1, 0.4), 0.15)); d = min(d, sdCircle(p, vec2( 0.5, 0.1), 0.35)); return d;}

    이 방법으로 많은 모양을 컴팩트하게 결합 할 수 있습니다. 이것이 이해되면,opU()기능이 사용되어야 하는데,이는’연산 조합’을 의미한다.

    이것은 가능한 것의 표면을 긁는 것입니다. 우리는 멋진 소프트 분 기능을 사용하여 부드러운 혼합을 얻을 수 있습니다-제공된opBlend()를 사용해보십시오. 적용 할 수있는 다른 많은 흥미로운 기술이있다,관심있는 독자는 자위대와 장면을 구축이 확장 소개라고합니다: https://www.youtube.com/watch?v=s8nFqwOho-s

    예::

    거리 필드를 사용하여 장면 데이터를 나타내는 방법과 레이마킹을 사용하여 장면과의 교차점을 찾는 방법에 대한 기본적인 이해를 얻었기를 바랍니다. 우리는 이제 3 차원으로 작업을 시작할 것입니다.진짜 마법이 일어나는 곳이죠.

    나중에 2 차원 시각화를 참조할 수 있도록 현재 셰이더를 저장하고 새 셰이더를 시작하는 것이 좋습니다.대부분의 헬퍼들은 새로운 쉐이더에 복사하여vec2초와vec3초를 교환하여 3 차원에서 작동하도록 만들 수 있습니다.

    레이 마칭 루프

    레이 행진을 구현하는 방법에 대한 기본 아이디어는 다음과 같습니다(의사 코드에서):

    Main function Evaluate camera Call RenderRayRenderRay function Raymarch to find intersection of ray with scene Shade

    이 단계들은 이제 각각 더 상세하게 설명될 것이다.

    카메라

    vec3 getCameraRayDir(vec2 uv, vec3 camPos, vec3 camTarget){ // Calculate camera's "orthonormal basis", i.e. its transform matrix components vec3 camForward = normalize(camTarget - camPos); vec3 camRight = normalize(cross(vec3(0.0, 1.0, 0.0), camForward)); vec3 camUp = normalize(cross(camForward, camRight)); float fPersp = 2.0; vec3 vDir = normalize(uv.x * camRight + uv.y * camUp + camForward * fPersp); return vDir;}

    이 기능은 먼저 카메라의’보기’매트릭스의 세 축,즉 앞,오른쪽 및 위쪽 벡터를 계산합니다.정방향 벡터는 카메라 위치에서 룩 대상 위치까지의 정규화된 벡터입니다.오른쪽 벡터는 전진 벡터를 월드 업 축과 교차시켜 찾을 수 있습니다.전방 및 우측 벡터는 카메라 업 벡터를 얻기 위해 교차된다.

    마지막으로 카메라 광선은 이 프레임을 사용하여 카메라 앞의 점을 취하고 픽셀 좌표uv을 사용하여 카메라 오른쪽 및 위쪽 방향으로 오프셋하여 계산됩니다.fPersp카메라의 시야를 간접적으로 제어 할 수 있습니다. 이 곱셈은 가까운 평면을 카메라에서 더 가까이 그리고 더 멀리 움직이는 것으로 생각할 수 있습니다. 결과를 보기 위하여 다른 가치에 실험.

    장면 정의

    float sdSphere(vec3 p, float r){ return length(p) - r;} float sdf(vec3 pos){ float t = sdSphere(pos-vec3(0.0, 0.0, 10.0), 3.0); return t;}

    당신이 볼 수 있듯이,우리는 우리의 입력 포인트의 구성 요소의 수에 대한sdCircle저장과 동일sdSphere()를 추가했습니다.

    레이마칭

    의사 코드:

    castRay for i in step count: sample scene if within threshold return dist return -1

    이 자신을 작성하려고-당신은 단지 다음 아래의 솔루션을 살펴 박히 경우.

    실제 코드:

    float castRay(vec3 rayOrigin, vec3 rayDir){ float t = 0.0; // Stores current distance along ray for (int i = 0; i < 64; i++) { float res = SDF(rayOrigin + rayDir * t); if (res < (0.0001*t)) { return t; } t += res; } return -1.0;}

    이제render함수를 추가합니다.이 함수는 결국 발견 된 교차점을 음영 처리합니다. 지금 그러나,우리가 트랙에있어 확인하기 위해 현장까지의 거리를 표시 할 수 있습니다. 우리는 그 차이를 더 잘 볼 수 있도록 크기를 조정하고 반전시킬 것입니다.

    vec3 render(vec3 rayOrigin, vec3 rayDir){ float t = castRay(rayOrigin, rayDir); // Visualize depth vec3 col = vec3(1.0-t*0.075); return col;}

    각 광선의 방향을 계산하려면 픽셀 좌표 입력fragCoord, 범위에서 변환하고 싶습니다.이 범위에서wh은 화면의 너비와 높이(픽셀 단위)이고a은 화면의 종횡비입니다. 그런 다음이 도우미에서 반환 된 값을 광선 방향을 얻기 위해 위에서 정의한getCameraRayDir함수로 전달할 수 있습니다.

    vec2 normalizeScreenCoords(vec2 screenCoord){ vec2 result = 2.0 * (screenCoord/iResolution.xy - 0.5); result.x *= iResolution.x/iResolution.y; // Correct for aspect ratio return result;}

    우리의 주요 이미지 기능은 다음과 같이 보입니다:

    void mainImage(out vec4 fragColor, vec2 fragCoord){ vec3 camPos = vec3(0, 0, -1); vec3 camTarget = vec3(0, 0, 0); vec2 uv = normalizeScreenCoords(fragCoord); vec3 rayDir = getCameraRayDir(uv, camPos, camTarget); vec3 col = render(camPos, rayDir); fragColor = vec4(col, 1); // Output to screen}

    운동:

    • 단계 수를 실험하고 결과가 어떻게 변하는 지 관찰하십시오.
    • 종료 임계값을 실험하고 결과가 어떻게 변하는지를 관찰한다.

    주변 용어

    장면에 약간의 색상을 얻으려면 먼저 객체와 배경을 구분할 것입니다.

    이를 위해 캐스트레이에서-1 을 반환하여 아무 것도 명중하지 않았다는 신호를 보낼 수 있습니다. 그런 다음 렌더링에서 해당 경우를 처리 할 수 있습니다.

    vec3 render(vec3 rayOrigin, vec3 rayDir){ vec3 col; float t = castRay(rayOrigin, rayDir); if (t == -1.0) { // Skybox colour col = vec3(0.30, 0.36, 0.60) - (rayDir.y * 0.7); } else { vec3 objectSurfaceColour = vec3(0.4, 0.8, 0.1); vec3 ambient = vec3(0.02, 0.021, 0.02); col = ambient * objectSurfaceColour; } return col;}

    https://www.shadertoy.com/view/4tdBzj

    확산 용어

    보다 사실적인 조명을 얻으려면 기본 램버트 조명을 계산할 수 있도록 표면 법선을 계산합시다.

    법선을 계산하기 위해 세 축 모두에서 표면의 기울기를 계산할 것입니다.

    이것이 실제로 의미하는 것은 자위대를 4 번 더 샘플링하는 것인데,각각은 우리의 1 차 광선으로부터 약간 오프셋된다.

    vec3 calcNormal(vec3 pos){ // Center sample float c = sdf(pos); // Use offset samples to compute gradient / normal vec2 eps_zero = vec2(0.001, 0.0); return normalize(vec3( sdf(pos + eps_zero.xyy), sdf(pos + eps_zero.yxy), sdf(pos + eps_zero.yyx) ) - c);}

    법선을 검사하는 한 가지 좋은 방법은 색상을 나타내는 것처럼 표시하는 것입니다. 모니터가 음수 색상 값을 표시 할 수 없기 때문에에서로 가져온 스케일링 및 바이어스 노멀을 표시 할 때 구가 어떻게 생겨야하는지입니다)

    col = N * vec3(0.5) + vec3(0.5);

    이제 우리는 법선을 가지고,우리는 그것과 빛의 방향 사이의 내적을 취할 수 있습니다.

    이것은 표면이 빛을 얼마나 직접적으로 마주보고 있는지,따라서 그것이 얼마나 밝아야 하는지를 말해 줄 것입니다.

    이 값의 최대 값을 0 으로 취하여 음수 값이 물체의 어두운면에 원치 않는 영향을 미치지 않도록합니다.

    // L is vector from surface point to light, N is surface normal. N and L must be normalized!float NoL = max(dot(N, L), 0.0);vec3 LDirectional = vec3(0.9, 0.9, 0.8) * NoL;vec3 LAmbient = vec3(0.03, 0.04, 0.1);vec3 diffuse = col * (LDirectional + LAmbient);

    쉽게 간과 할 수있는 렌더링의 매우 중요한 부분 중 하나는 감마 보정입니다. 모니터로 전송되는 픽셀 값은 감마 공간에 있으며,이는 인간이 덜 민감한 강도 범위에서 적은 비트를 사용하여 정밀도를 극대화하는 데 사용되는 비선형 공간입니다.

    모니터는”선형”공간에서 작동하지 않기 때문에 색상을 출력하기 전에 감마 곡선을 보정해야합니다. 그 차이는 매우 눈에 띄며 항상 수정해야합니다. 현실에서 우리는 특정 디스플레이 장치에 대한 감마 곡선이 모르는,그래서 디스플레이 기술과 전체 상황은 끔찍한 혼란이다(따라서 많은 게임에서 감마 튜닝 단계),하지만 일반적인 가정은 다음과 같은 감마 곡선입니다:

    상수 0.4545 는 단순히 1.0 / 2.2

    col = pow(col, vec3(0.4545)); // Gamma correction

    https://www.shadertoy.com/view/4t3fzn

    그림자

    그림자를 계산하기 위해,우리는 우리가 장면을 교차 지점에서 시작하여 광원의 방향으로 가고 광선을 발사 할 수 있습니다.

    이 광선 행진으로 인해 우리가 무언가를 치면 빛도 막혀서 이 픽셀이 그림자에 있음을 알 수 있습니다.

    float shadow = 0.0;vec3 shadowRayOrigin = pos + N * 0.01;vec3 shadowRayDir = L;IntersectionResult shadowRayIntersection = castRay(shadowRayOrigin, shadowRayDir);if (shadowRayIntersection.mat != -1.0){ shadow = 1.0;}col = mix(col, col*0.2, shadow);

    그라운드 평면

    그라운드 평면을 추가하여 구체에 의해 그림자를 더 잘 볼 수 있습니다.

    평면이 원점으로부터의 거리를 나타냅니다.

    float sdPlane(vec3 p, vec4 n){ return dot(p, n.xyz) + n.w;}

    부드러운 그림자

    실생활에서 그림자는 즉시 멈추지 않으며,반감기라고 불리는 약간의 감쇠가 있습니다.

    우리는 각각 약간 다른 방향으로 표면 지점에서 여러 개의 광선을 행진함으로써 이것을 모델링 할 수 있습니다.

    그런 다음 결과를 합산하고 반복 횟수를 평균 할 수 있습니다. 이렇게 하면 그림자의 가장자리가

    일부 광선이 맞고 다른 광선이 놓치면 50%의 어둠이 나타납니다.

    다소 의사 난수를 찾는 것은 여러 가지 방법으로 수행 할 수 있습니다.:

    float rand(vec2 co){ return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);}

    이 함수는[0,1)범위의 숫자를 반환합니다. 가장 바깥쪽 연산은 부동 소수점 숫자의 소수 구성 요소를 반환하는 프랙트이기 때문에 출력이 이 범위에 바인딩된다는 것을 알고 있습니다.

    이를 사용하여 다음과 같이 그림자 광선을 계산할 수 있습니다:

    float shadow = 0.0;float shadowRayCount = 1.0;for (float s = 0.0; s < shadowRayCount; s++){ vec3 shadowRayOrigin = pos + N * 0.01; float r = rand(vec2(rayDir.xy)) * 2.0 - 1.0; vec3 shadowRayDir = L + vec3(1.0 * SHADOW_FALLOFF) * r; IntersectionResult shadowRayIntersection = castRay(shadowRayOrigin, shadowRayDir); if (shadowRayIntersection.mat != -1.0) { shadow += 1.0; }}col = mix(col, col*0.2, shadow/shadowRayCount);

    텍스처 매핑

    전체 표면에 걸쳐 단일 표면 색상(또는 다른 특성)을 균일하게 정의하는 대신 텍스처를 사용하여 표면에 적용 할 패턴을 정의 할 수 있습니다.우리는 이것을 달성하는 세 가지 방법을 다룰 것입니다.

    3 차원 텍스처 매핑

    채널에 할당할 수 있는 셰이더토이에서 쉽게 액세스할 수 있는 볼륨 텍스처가 있습니다. 표면 점의 3 차원 위치를 사용하여 이러한 텍스처 중 하나를 샘플링 해보십시오:

    // assign a 3D noise texture to iChannel0 and then sample based on world positionfloat textureFreq = 0.5;vec3 surfaceCol = texture(iChannel0, textureFreq * surfacePos).xyz;

    노이즈를 샘플링하는 한 가지 방법은 다음과 같은 것을 사용하여 여러 스케일을 함께 추가하는 것입니다:

    // assign a 3D noise texture to iChannel0 and then sample based on world positionfloat textureFreq = 0.5;vec3 surfaceCol = 0.5 * texture(iChannel0, 1.0 * textureFreq * surfacePos).xyz + 0.25 * texture(iChannel0, 2.0 * textureFreq * surfacePos).xyz + 0.125 * texture(iChannel0, 4.0 * textureFreq * surfacePos).xyz + 0.0625 * texture(iChannel0, 8.0 * textureFreq * surfacePos).xyz ;

    위의 상수/가중치는 일반적으로 프랙탈 노이즈에 사용되지만 원하는 값을 취할 수 있습니다. 무게/저울/색상을 실험하고 달성 할 수있는 흥미로운 효과를 확인하십시오.

    아이타임을 사용하여 오브젝트에 애니메이션을 적용하고 볼륨 텍스처가 어떻게 동작하는지 관찰해 보십시오. 이 동작을 변경할 수 있습니까?

    2 차원 텍스처 매핑

    2 차원 텍스처를 적용하는 것은 흥미로운 문제입니다. 각 삼각형에는 삼각형에 매핑해야 하는 텍스처 영역의 좌표를 제공하는 하나 이상의 유티브가 할당되어 있습니다(텍스처 매핑). 그래서 텍스처를 샘플링하는 방법을 알아내야 합니다.

    한 가지 방법은 위에서 아래로 세계 투영을 사용하여 텍스처를 샘플링하는 것입니다.:

    // top down projectionfloat textureFreq = 0.5;vec2 uv = textureFreq * surfacePos.xz; // sample texturevec3 surfaceCol = texture2D(iChannel0, uv).xyz;

    이 접근 방식을 사용하면 어떤 제한이 있습니까?

    삼중 평면 매핑

    텍스처를 매핑하는 더 진보 된 방법은 기본 축에서 3 개의 투영을 수행 한 다음 삼중 평면 매핑을 사용하여 결과를 혼합하는 것입니다. 혼합의 목표는 표면의 각 점에 가장 적합한 질감을 선택하는 것입니다. 한 가지 가능성은 각 월드 축에 대한 서피스 법선의 정렬을 기반으로 블렌드 가중치를 정의하는 것입니다. 축 중 하나를 사용하여 앞면을 향하게 하는 서피스는 큰 혼합 무게를 받습니다:

    vec3 triplanarMap(vec3 surfacePos, vec3 normal){ // Take projections along 3 axes, sample texture values from each projection, and stack into a matrix mat3 triMapSamples = mat3( texture(iChannel0, surfacePos.yz).rgb, texture(iChannel0, surfacePos.xz).rgb, texture(iChannel0, surfacePos.xy).rgb ); // Weight three samples by absolute value of normal components return triMapSamples * abs(normal);}

    이 접근 방식을 사용하면 어떤 제한이 있습니까?

    재료

    캐스트레이 함수로부터 되돌아오는 거리와 함께,오브젝트 적중의 재질을 나타내는 인덱스를 반환할 수도 있습니다. 이 색인을 사용하여 그에 따라 개체를 색칠 할 수 있습니다.

    우리의 연산자는 부동 소수점보다는 벡터 2 를 가져 와서 각각의 첫 번째 구성 요소를 비교해야합니다.

    이제 씬을 정의할 때 각 프리미티브에 대한 재질도 지정합니다.:

    vec2 res = vec2(sdSphere(pos-vec3(3,-2.5,10), 2.5), 0.1);res = opU(res, vec2(sdSphere(pos-vec3(-3, -2.5, 10), 2.5), 2.0));res = opU(res, vec2(sdSphere(pos-vec3(0, 2.5, 10), 2.5), 5.0));return res;

    그런 다음 이 재질 인덱스에 렌더링 함수의 일부 값을 곱하여 각 개체에 대해 서로 다른 색상을 얻을 수 있습니다. 다른 값을 시도하십시오.

    col = vec3(0.18*m, 0.6-0.05*m, 0.2)if (m == 2.0){ col *= triplanarMap(pos, N, 0.6);}

    바둑판 패턴을 사용하여 접지 평면을 색칠합시다. 이 멋진 분석적 앤티 앨리어싱 체크 박스 기능을 이니 고 퀼 레즈 웹 사이트에서 가져 왔습니다.

    float checkers(vec2 p){ vec2 w = fwidth(p) + 0.001; vec2 i = 2.0*(abs(fract((p-0.5*w)*0.5)-0.5)-abs(fract((p+0.5*w)*0.5)-0.5))/w; return 0.5 - 0.5*i.x*i.y;}

    우리는 그 차원에서 반복 패턴을 얻기 위해 우리의 평면 위치의 엑스 지 구성 요소에 전달합니다.

    https://www.shadertoy.com/view/Xl3fzn

    안개

    각 교차로가 카메라에서 얼마나 멀리 발생했는지에 따라 장면에 안개를 추가 할 수 있습니다.

    다음과 비슷한 것을 얻을 수 있는지 확인하십시오:

    https://www.shadertoy.com/view/Xtcfzn

    모양&재료 혼합 최소 연산자에 의해 주어진 가혹한 주름을 피하기 위해,우리는 모양을 원활 하 게 혼합 하는 더 정교한 연산자를 사용할 수 있습니다.

    // polynomial smooth min (k = 0.1);float sminCubic(float a, float b, float k){ float h = max(k-abs(a-b), 0.0); return min(a, b) - h*h*h/(6.0*k*k);} vec2 opBlend(vec2 d1, vec2 d2){ float k = 2.0; float d = sminCubic(d1.x, d2.x, k); float m = mix(d1.y, d2.y, clamp(d1.x-d,0.0,1.0)); return vec2(d, m);}

    앤티 앨리어싱

    약간 오프셋 된 카메라 방향 벡터로 장면을 여러 번 샘플링하면 앨리어싱을 피하는 부드러운 값을 얻을 수 있습니다.

    필자는 루프에서 더 명확하게 호출 할 수 있도록 장면 색상 계산을 자체 함수로 가져 왔습니다.

    float AA_size = 2.0;float count = 0.0;for (float aaY = 0.0; aaY < AA_size; aaY++){ for (float aaX = 0.0; aaX < AA_size; aaX++) { fragColor += getSceneColor(fragCoord + vec2(aaX, aaY) / AA_size); count += 1.0; }}fragColor /= count;

    스텝 카운트 최적화

    빨간색으로 각 픽셀에 대해 얼마나 많은 단계를 수행하는지 시각화하면 아무 것도 치지 않는 광선이 대부분의 반복을 담당한다는 것을 분명히 알 수 있습니다.

    이것은 특정 장면에 대한 상당한 성능 향상을 제공 할 수 있습니다.

    if (t > drawDist) return backgroundColor;

    모양&재료 보간

    혼합 기능을 사용하여 두 모양 사이를 보간하고 시간이 지남에 따라 변조 할 수 있습니다.

    vec2 shapeA = vec2(sdBox(pos-vec3(6.5, -3.0, 8), vec3(1.5)), 1.5);vec2 shapeB = vec2(sdSphere(pos-vec3(6.5, -3.0, 8), 1.5), 3.0);res = opU(res, mix(shapeA, shapeB, sin(iTime)*0.5+0.5));

    도메인 반복

    부호있는 거리 필드를 사용하여 모양을 반복하는 것은 매우 쉽습니다.

    이 기술은 예를 들어 장면의 표현 크기를 증가시키지 않고 열을 여러 번 반복하는 데 사용할 수 있습니다.

    여기서 입력 위치의 세 가지 구성 요소를 모두 반복 한 다음 빼기 연산자(최대())를 사용하여 반복을 경계 상자로 제한했습니다.

    하나의 잡았다 당신이 반으로 잘라하지 않도록 당신의 모양에 반복을 중심으로하기 위해에 의해 변조되는 값의 절반을 뺄 필요가 있다는 것입니다.

    float repeat(float d, float domain){ return mod(d, domain)-domain/2.0;}

    후 처리 효과

    비 네트

    화면 중앙에서 멀리 떨어진 픽셀을 어둡게하면 간단한 비 네트 효과를 얻을 수 있습니다.

    대비

    더 어둡고 밝은 값이 강조되어 이미지의 강도와 함께 인지된 동적 범위가 증가할 수 있습니다.

    col = smoothstep(0.0,1.0,col);

    “앰비언트 오 클루 전”

    위의 이미지(최적화)의 역수를 취하면 이상한 아오 유사 효과를 얻을 수 있습니다.

    col *= (1.0-vec3(steps/maxSteps));

    광고 인해서

    당신이 볼 수 있듯이,많은 후 처리 효과는 하찮게 구현 할 수 있습니다;다른 기능과 함께 놀러 당신이 만들 수있는 다른 어떤 효과를 참조하십시오.

    www.shadertoy.com/view/MtdBzs

    다음은?

    우리는 여기에 기초를 다뤘습니다;이 분야에서 탐구 할 것이 훨씬 더 있습니다:

    • 지하 산란
    • 주변 폐색
    • 애니메이션 프리미티브
    • 원시 뒤틀림 기능(트위스트,벤드,…)
    • 투명도(굴절,가성,…)
    • 최적화(경계 볼륨 계층 구조)

    쉐이더토이를 탐색하여 수행할 수 있는 작업에 대한 영감을 얻고 다양한 셰이더를 통해 다양한 효과가 구현되는 방식을 확인할 수 있습니다. 많은 쉐이더는 조정할 즉시 효과를 볼 수있는 변수가(고도 입력 컴파일 할 수있는 바로 가기입니다!).

    당신이 더 많은 학습에 관심이 있다면 또한 참조를 통해 읽기를 제공!

    읽어 주셔서 감사합니다! 우리에게 당신의 멋진 쉐이더를 보내해야합니다! 당신이 과정에 대한 의견이 있으면 우리는 또한 그것을 듣고 싶어요!2018 년 11 월 15 일(토)~2018 년 12 월 15 일(일)~2018 년 12 월 15 일(일)~2018 년 12 월 15 일(일)~2018 년 12 월 15 일(일)~2018 년 12 월 15 일(일)

    추천 도서:

    자위대 기능: http://jamie-wong.com/2016/07/15/ray-marching-signed-distance-functions/

    클레이북 데모: https://www.youtube.com/watch?v=Xpf7Ua3UqOA

    한 주말에 레이 트레이싱: http://in1weekend.blogspot.com/2016/01/ray-tracing-in-one-weekend.html

    1999 년 11 월 15 일-1999 년 11 월 15 일-1999 년 11 월 15 일-1999 년 11 월 15 일

    답글 남기기

    이메일 주소는 공개되지 않습니다.