UFO ET IT

WebGL 및 ThreeJS의 개선 된 영역 조명

ufoet 2020. 12. 9. 20:57
반응형

WebGL 및 ThreeJS의 개선 된 영역 조명


이 데모와 유사한 WebGL에서 영역 조명 구현 작업을 해왔습니다.

http://threejs.org/examples/webgldeferred_arealights.html

three.js의 위 구현은 gamedev.net에서 ArKano22의 작업에서 포팅되었습니다.

http://www.gamedev.net/topic/552315-glsl-area-light-implementation/

이러한 솔루션은 매우 인상적이지만 둘 다 몇 가지 제한 사항이 있습니다. ArKano22의 원래 구현의 주요 문제는 확산 항의 계산이 표면 법선을 고려하지 않는다는 것입니다.

저는이 문제를 해결하기 위해 redPlant의 개선 사항과 함께 작업하면서 몇 주 동안이 솔루션을 보강 해 왔습니다. 현재 나는 정상적인 계산을 솔루션에 통합했지만 결과도 결함이 있습니다.

다음은 현재 구현에 대한 간략한 미리보기입니다.

지역 조명 티저

소개

각 조각의 확산 항을 계산하는 단계는 다음과 같습니다.

  1. 영역 라이트가 놓인 평면에 정점을 투영하여 투영 된 벡터가 라이트의 법선 / 방향과 일치하도록합니다.
  2. 투영 벡터를 라이트의 법선과 비교하여 정점이 영역 라이트 평면의 올바른면에 있는지 확인합니다.
  3. 라이트의 중심 / 위치에서 평면에 투영 된이 점의 2D 오프셋을 계산합니다.
  4. 이 2D 오프셋 벡터를 고정하여 라이트 영역 (폭과 높이로 정의 됨) 내부에 위치하도록합니다.
  5. 투영되고 고정 된 2D 점의 3D 세계 위치를 유도합니다. 이것은 영역 조명에서 정점에 가장 가까운 점 입니다.
  6. 정점 간 가장 가까운 점 벡터 (정규화)와 정점 법선 사이의 내적을 취하여 점 광원에 대해 수행하는 일반적인 확산 계산을 수행합니다.

문제

이 솔루션의 문제는 조명 계산이 가장 가까운 지점 에서 수행되고 조각을 더 비출 수있는 조명 표면의 다른 지점을 고려하지 않는다는 것입니다. 이유를 설명해 보겠습니다 ...

다음 다이어그램을 고려하십시오.

문제 지역 조명 상황

영역 조명은 표면에 수직이고 교차합니다. 표면의 각 조각은 항상 표면과 조명이 교차하는 영역 조명에서 가장 가까운 지점반환 합니다. 표면 법선과 정점-광 벡터는 항상 수직이므로 이들 사이의 내적은 0입니다. 그 결과, 표면에 빛의 넓은 영역이 있음에도 불구하고 확산 기여도의 계산은 0입니다.

잠재적 솔루션

저는 영역 광의 가장 가까운 지점 에서 빛을 계산하기보다는 정점-광 벡터 (정규화)와 정점 법선 사이에서 가장 큰 내적을 산출하는 영역 광의 한 지점에서 계산할 것을 제안합니다 . 위의 다이어그램에서 이것은 파란색 점이 아니라 보라색 점입니다.

도움!

그래서 여기가 당신의 도움이 필요합니다. 내 머릿속에서는이 점이 어떻게 도출 될 수 있는지 꽤 잘 알고 있지만 해답에 도달 할 수있는 수학적 능력은 없습니다.

현재 조각 셰이더에서 다음 정보를 사용할 수 있습니다.

  • 정점 위치
  • 정점 법선 (단위 벡터)
  • 조명 위치, 너비 및 높이
  • 라이트 노멀 (단위 벡터)
  • 라이트 라이트 (단위 벡터)
  • 점등 (단위 벡터)
  • 정점에서 라이트 평면 (3D)으로 투영 된 점
  • 라이트 중심에서 투영 된 점 오프셋 (2D)
  • 고정 오프셋 (2D)
  • 이 고정 오프셋의 세계 위치 – 가장 가까운 점 (3D)

이 모든 정보를 시각적 컨텍스트에 담기 위해 다음 다이어그램을 만들었습니다 (도움이 되길 바랍니다).

사용 가능한 조명 정보

내 제안을 테스트하려면 빨간색 점으로 표시되는 영역 조명 캐스팅 지점이 필요합니다 . 그러면 정점 대 캐스팅 지점 (정규화)과 정점 법선 사이의 내적을 수행 할 수 있습니다. 다시 말하지만 이것은 가능한 최대 기여 값을 산출해야합니다.

최신 정보!!!

현재 구현 한 수학을 시각화하는 대화 형 스케치를 CodePen에서 만들었습니다.

http://codepen.io/wagerfield/pen/ywqCp

코드 펜

집중해야 할 관련 코드는 318입니다.

castingPoint.location의 인스턴스이며 THREE.Vector3퍼즐의 누락 된 조각입니다. 스케치의 왼쪽 아래에 2 개의 값이 있음을 알 수 있습니다.이 값은 관련 벡터 사이의 내적을 표시하도록 동적으로 업데이트됩니다.

나는 솔루션이 정점 법선의 방향과 정렬되고 빛의 평면에 수직 인 또 다른 의사 평면을 필요로한다고 생각하지만, 내가 틀릴 수 있습니다!


좋은 소식은 해결책이 있다는 것입니다. 하지만 먼저 나쁜 소식입니다.

내적을 최대화하는 점을 사용하는 방법은 근본적으로 결함이 있으며 물리적으로 그럴듯하지 않습니다.

위의 첫 번째 그림에서 영역 조명이 왼쪽 절반으로 만 구성되었다고 가정합니다.

"보라색"점 (왼쪽 절반의 내적을 최대화하는 점)은 결합 된 두 절반의 내적을 최대화하는 점과 동일합니다.

따라서 제안 된 솔루션을 사용하는 경우 영역 라이트의 왼쪽 절반이 전체 라이트와 동일한 복사를 방출한다고 결론을 내릴 수 있습니다. 당연히 불가능합니다.

영역 조명이 주어진 지점에 투사하는 총 빛의 양을 계산하는 솔루션은 다소 복잡하지만 참고로 1994 년 논문 The Irradiance Jacobian for Partially Occluded Polyhedral Sources 에서 설명을 찾을 수 있습니다 .

그림 11.2 절의 몇 단락 을 살펴본 다음 중지하십시오. :-)

쉽게하기 위해 WebGLRenderer지연된 것이 아닌 three.js를 사용하여 솔루션을 구현하는 매우 간단한 셰이더를 코딩했습니다 .

편집 : 여기에 업데이트 된 바이올린이 있습니다 : http://jsfiddle.net/hh74z2ft/1/

여기에 이미지 설명 입력

조각 셰이더의 핵심은 매우 간단합니다.

// direction vectors from point to area light corners

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

    lPosition[ i ] = viewMatrix * lightMatrixWorld * vec4( lightverts[ i ], 1.0 ); // in camera space

    lVector[ i ] = normalize( lPosition[ i ].xyz + vViewPosition.xyz ); // dir from vertex to areaLight

}

// vector irradiance at point

vec3 lightVec = vec3( 0.0 );

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

    vec3 v0 = lVector[ i ];
    vec3 v1 = lVector[ int( mod( float( i + 1 ), float( NVERTS ) ) ) ]; // ugh...

    lightVec += acos( dot( v0, v1 ) ) * normalize( cross( v0, v1 ) );

}

// irradiance factor at point

float factor = max( dot( lightVec, normal ), 0.0 ) / ( 2.0 * 3.14159265 );

더 좋은 소식 :

  1. 이 접근 방식은 물리적으로 정확합니다.
  2. 감쇠는 자동으로 처리됩니다. (작은 조명에는 더 큰 강도 값이 필요합니다.)
  3. 이론적으로이 접근 방식은 직사각형뿐 아니라 임의의 다각형에서도 작동해야합니다.

주의 사항 :

  1. 귀하의 질문이 다루는 내용이기 때문에 나는 확산 구성 요소 만 구현했습니다.
  2. 이미 코딩 한 것과 비슷한 합리적인 휴리스틱을 사용하여 스페 큘러 컴포넌트를 구현해야합니다.
  3. This simple example does not handle the case where the area light is "partially below the horizon" -- i.e. not all 4 vertices are above the plane of the face.
  4. Since WebGLRenderer does not support area lights, you can't "add the light to the scene" and expect it to work. This is why I pass all necessary data into the custom shader. ( WebGLDeferredRenderer does support area lights, of course. )
  5. Shadows are not supported.

three.js r.73


Hm. Odd question! It seems like you started out with a very specific approximation and are now working your way backward to the right solution.

If we stick to only diffuse and a surface that is flat (has only one normal) what is the incoming diffuse light? Even if we stick to every incoming light has a direction and intensity, and we just take allin = integral(lightin) ((lightin).(normal))*light this is hard. so the whole problem is solving this integral. with point light you cheat by making it a sum and pulling the light out. That works fine for point lights without shadows etc. now what you really want to do is to solve that integral. that's what you can do with some kind of light probes, spherical harmonics or many other techniques. or some tricks to estimate the amount of light from a rectangle.

For me it always helps to think of the hemisphere above the point you want to light. You need all of the light coming in. Some is less important, some more. That's what your normal is for. In a production raytracer you could just sample a few thousand points and have a good guess. In realtime you have to guess a lot faster. And that's what your library code does: A quick choice for a good (but flawed) guess.

And that's where I think you are going backwards: You realized that they are making a guess, and that it sucks sometimes (that's the nature of guessing). Now, don't try to fix their guess, but come up with a better one! And maybe try to understand why they picked that guess. A good approximation is not about being good at corner cases but at degrading well. That's what this one looks like to me. (Again, sorry, I'm to lazy to read the three.js code now).

So to answer your question:

  • I think you are going about it the wrong way. You are starting with a highly optimized idea, and are trying to fix that. Better to start from the problem.
  • Solve one thing at a time. Your screenshot has a lot of specular, that is unrelated to your problem but very visual and was probably a big influence to the people designing the model.
  • You are on the right track and have a much better idea about rendering than most people. That can work for and against you. Read up on some modern game engines and their lighting models. You will always find a fascinating combination of hacks and deep understanding. The deep understanding is what drives picking the right hacks :)

Hope this helps. I might be totally wrong here and rambling at somebody who is just looking for some quick math, in that case I apologize.


Let's agree that casting point is always on the edge.

Let's say that "lit part" is the part of space that is represented by extruded light's quad along its normal.

If surface point sits in the lit part, then you need to calculate the plane that holds that point, it's normal vector and light's normal. Intersection between that plane and light's would give you two points as options (only two, because casting point is always on the edge). So test those two to see which one contributes more.

If the point is not in the lit part, then you could calculate four planes, each has surface point, its normal and one of the vertices of the light's quad. For each light-quad vertex you would have two points (vertex + one more intersection point) to test which contributes the most.

This should do the trick. Please give me feedback if you encounter any counterexample.


http://s3.hostingkartinok.com/uploads/images/2013/06/9bc396b71e64b635ea97725be8719e79.png

If I understand correctly:

L "점 x0에 대한 빛"정의

패 ~ K / S ^ 2

S = sqrt (y ^ 2 + x0 ^ 2)

L = sum (k / (sqrt (y ^ 2 + x0 ^ 2)) ^ 2), y = 0 .. 무한대

L = sum (k / (y ^ 2 + x0 ^ 2)), y = 0 .. 무한대, x> 0, y> 0

L = 적분 (k / (y ^ 2 + x0 ^ 2)), y = 0..infinity = k * Pi / (2 * x0)

http://s5.hostingkartinok.com/uploads/images/2013/06/6dbb7b6d3babc092d3daf18bb3c6e6d5.png

대답:

L = k * Pi / (2 * x0)

k 는 환경에 따라 다름


꽤 오래되었지만 GPU gems 5에는 "가장 가까운 지점"이 아닌 "가장 중요한 지점"을 사용하여 영역 조명의 적분을 근사화하는 기사가 있습니다.

http://gpupro.blogspot.com/2014/03/gpu-pro-5-physically-based-area-lights.html

참고 URL : https://stackoverflow.com/questions/17021264/improved-area-lighting-in-webgl-threejs

반응형