부드러운 2D 조명 효과를 얻으려면 어떻게해야합니까?


18

XNA에서 2D 타일 기반 게임을 만들고 있습니다.

현재 내 번개는 다음과 같습니다 .

어떻게 이렇게 보이게 할 수 있습니까?

각각의 색조를 갖는 각 블록 대신 부드러운 오버레이가 있습니다.

나는 일종의 쉐이더를 가정하고 주변 타일의 조명 값을 쉐이더에 전달하려고하지만 쉐이더가있는 초보자이므로 확실하지 않습니다.

현재 조명은 빛을 계산 한 다음 a로 전달 SpriteBatch하고 tint 매개 변수를 사용하여 그립니다. 각 타일에는 Color색조에 사용되는 조명 알고리즘에서 그리기 전에 계산 된 타일이 있습니다.

다음은 현재 조명을 계산하는 방법에 대한 예입니다 (왼쪽, 오른쪽 및 아래쪽 에서도이 작업을 수행하지만 프레임 별로이 작업을 수행하는 데 정말로 지쳤습니다 ...)

여기에 이미지 설명을 입력하십시오

실제로 빛을 가져 와서 그리는 것은 아무 문제없습니다 !

나는 안개의 전쟁과 그라디언트 원형 오버레이를 사용하여 부드러운 번개를 만드는 수많은 자습서를 보았지만 각 타일에 번개 값을 할당하는 좋은 방법이 이미 있습니다. 그들 사이에서 부드럽게해야합니다.

그래서 검토

  • 조명 계산 (완료)
  • 타일 ​​그리기 (완료, 쉐이더에 맞게 타일을 수정해야한다는 것을 알고 있습니다)
  • 음영 타일 (값을 전달하고 "그라데이션"을 적용하는 방법)

답변:


6

이런 건 어때?

타일 ​​스프라이트를 착색하여 조명을 그리지 마십시오. 당신의 그리기 꺼져 , 렌더링 타겟 타일을 한 후 타일의 지역을 커버 그레이 스케일 사각형 각 하나를 나타내는 두 번째 렌더 타겟에 타일 등을 그립니다. 최종 장면을 렌더링하려면 셰이더를 사용하여 두 렌더링 대상을 결합하여 두 번째 값에 따라 첫 번째 픽셀을 어둡게합니다.

이것은 당신이 지금 가진 것을 정확하게 생산할 것입니다. 그것은 당신에게 도움이되지 않으므로 조금 변경합시다.

타일 이 직사각형 영역이 아닌 단일 픽셀로 표시 되도록 라이트 맵 렌더 대상의 크기를 변경하십시오 . 최종 장면을 합성 할 때는 선형 필터링과 함께 샘플러 상태를 사용하십시오. 그렇지 않으면 다른 모든 것을 그대로 두십시오.

셰이더를 올바르게 작성했다고 가정하면 합성 중에 라이트 맵이 효과적으로 "확대"되어야합니다. 그래픽 장치의 텍스처 샘플러를 통해 멋진 그라디언트 효과를 무료로 얻을 수 있습니다.

셰이더를 잘라 내고 '어두워 진'BlendState를 사용하여이 작업을 더 간단하게 수행 할 수도 있지만, 구체적으로 설명하기 전에 실험해야합니다.

최신 정보

나는 오늘 이것을 실제로 조롱 할 시간이 있었다. 위의 답변은 모든 것에 대한 첫 번째 답변으로 쉐이더를 사용하는 습관을 반영하지만이 경우 실제로 필요하지 않으며 사용이 불필요하게 복잡합니다.

내가 제안했듯이, 사용자 정의 BlendState를 사용하여 정확히 동일한 효과를 얻을 수 있습니다. 구체적으로,이 커스텀 BlendState는 :

BlendState Multiply = new BlendState()
{
    AlphaSourceBlend = Blend.DestinationAlpha,
    AlphaDestinationBlend = Blend.Zero,
    AlphaBlendFunction = BlendFunction.Add,
    ColorSourceBlend = Blend.DestinationColor,
    ColorDestinationBlend = Blend.Zero,
    ColorBlendFunction = BlendFunction.Add
}; 

블렌딩 방정식은

result = (source * sourceBlendFactor) blendFunction (dest * destBlendFactor)

커스텀 BlendState를 사용하면

result = (lightmapColor * destinationColor) + (0)

즉, 순수한 흰색 (1, 1, 1, 1)의 원본 색상은 대상 색상을 보존하고, 순수한 검정색 (0, 0, 0, 1)의 원본 색상은 대상 색상을 순수한 검정색으로 어둡게합니다. 중간에 회색 음영이 있으면 대상 색상이 중간 정도 어두워집니다.

이것을 실제로 적용하려면 먼저 라이트 맵을 만들기 위해 필요한 모든 작업을 수행하십시오.

var lightmap = GetLightmapRenderTarget();

그런 다음 평소와 같이 조명이없는 장면을 백 버퍼로 직접 그립니다 .

spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend);
/* draw the world here */
spriteBatch.End();

그런 다음 커스텀 BlendState를 사용하여 라이트 맵을 그립니다 :

var offsetX = 0; // you'll need to set these values to whatever offset is necessary
var offsetY = 0; // to align the lightmap with the map tiles currently being drawn
var width = lightmapWidthInTiles * tileWidth;
var height = lightmapHeightInTiles * tileHeight;

spriteBatch.Begin(SpriteSortMode.Immediate, Multiply);
spriteBatch.Draw(lightmap, new Rectangle(offsetX, offsetY, width, height), Color.White);
spriteBatch.End();

그러면 대상 색상 (조명 타일)에 소스 색상 (라이트 맵)이 곱 해져서 조명이없는 타일이 적절하게 어두워지고 라이트 맵 텍스처가 필요한 크기로 조정되어 그라디언트 효과가 생성됩니다.


이것은 매우 간단 해 보입니다. 나중에 할 수있는 것을 볼 수 있습니다. 나는 전에 비슷한 것을 시도했다. (다른 모든 것에 라이트 맵을 렌더링하고 블러 셰이더를 사용했지만 renderTarget을 "투명"하게 만들 수 없었지만 자주색 오버레이가 있었지만 실제 타일 레이어를 통해보고 싶었습니다.)
Cyral

장치에서 렌더 대상을 설정 한 후 device.Clear (Color.Transparent);
Cole Campbell

이 경우 라이트 맵을 흰색 또는 검은 색으로 완전히 비워야하며, 각각 완전히 밝고 완전한 어둠이어야합니다.
Cole Campbell

나는 그것이 이전에 시도한 것이라고 생각하지만 여전히 자주색이었습니다. 내일이 일할 시간이 있으면 내일 살펴볼 것입니다.
Cyral

거의 모든 것이 해결되었지만 검은 색 대신 투명하지 않고 검은 색에서 흰색으로 희미 해집니다. 셰이더 부분은 내가 혼란스러워하는 부분이며, 원래의 조명이없는 장면을 새로운 장면의 장면으로 어둡게 만드는 방법을 작성합니다.
Cyral

10

여기에 간단하고 부드러운 2D 번개를 생성하고 세 단계로 렌더링하는 매우 간단한 방법이 있습니다.

  • 패스 1 : 번개없이 게임 세계.
  • 패스 2 : 세상없는 번개
  • 화면에 표시되는 1과 2의 조합.

패스 1에서는 모든 스프라이트와 터 레인을 그립니다.

패스 2에서는 광원 인 두 번째 스프라이트 그룹을 그립니다. 그것들은 다음과 비슷하게 보일 것입니다 :
여기에 이미지 설명을 입력하십시오
검은 색으로 렌더 타깃을 초기화하고 최대 또는 추가 블렌딩으로 스프라이트를 그 위에 그립니다.

패스 3에서는 두 개의 이전 패스를 결합합니다. 그것들을 결합하는 방법에는 여러 가지가 있습니다. 그러나 가장 단순하고 가장 예술적인 방법은 곱하기를 통해 함께 혼합하는 것입니다. 이것은 다음과 같습니다
여기에 이미지 설명을 입력하십시오


문제는, 이미 조명 시스템을 가지고 있는데 빛을 통과해야하는 물체와 그렇지 않은 물체 때문에 점 조명이 작동하지 않는 것입니다.
Cyral

2
더 까다 롭습니다. 그림자를 얻으려면 레이 캐스트를해야합니다. Teraria는 지형에 큰 블록을 사용하므로 아무런 문제가 없습니다. 부정확 한 레이 캐스팅을 사용할 수는 있지만 테라 리아보다 더 나아 보이지 않으며 그래픽을 차단하지 않으면 훨씬 덜 적합 할 수 있습니다. 하십시오 고급 좋은 찾고 기술은이 문서가 gamedev.net/page/resources/_/technical/...
API - 야수

2

게시 한 두 번째 링크 는 일종의 전쟁 안개 처럼 보입니다 . 이것에 대한 몇 가지 다른 질문이 있습니다.

플레이어가 앞으로 나아갈 때 (그 픽셀의 알파를 0으로 설정하여) 검은 오버레이의 비트를 "삭제" 하는 텍스처 처럼 보입니다 .

나는 그 사람이 플레이어가 탐험 한 브러시 유형 "지우기"를 사용해야한다고 말한다.


1
그것은 전쟁의 안개가 아니며 각 프레임마다 번개를 계산합니다. 내가 지적한 게임은 Terraria와 비슷합니다.
Cyral

내가 의미하는 바는 전쟁의 안개와 유사하게 구현 될 수 있다는 것입니다.
bobobobo

2

타일 ​​대신 밝은 정점 (타일 사이의 모서리). 4 개의 정점을 기준으로 각 타일에서 조명을 혼합합니다.

정점 에 대해 가시선 테스트를 수행하여 조명의 밝기를 결정합니다 (모든 조명을 차단하거나 조명을 감소시킬 수 있습니다. 순수 이진 가시 / 비가시 테스트).

타일을 렌더링 할 때 각 정점의 조명 값을 GPU로 보냅니다. 프래그먼트 셰이더를 사용하면 타일 스프라이트의 각 프래그먼트에서 보간 된 라이트 값을 가져와 각 픽셀을 부드럽게 비출 수 있습니다. GLSL을 만진 후 실제로 코드 샘플을 제공하는 것이 불편하다고 생각할 정도로 오래되었지만 의사 코드에서는 다음과 같이 간단합니다.

texture t_Sprite
vec2 in_UV
vec4 in_Light // coudl be one float; assuming you might want colored lights though

fragment shader() {
  output = sample2d(t_Sprite, in_UV) * in_Light;
}

버텍스 쉐이더는 입력 값을 프래그먼트 쉐이더에 전달하기 만하면됩니다.

더 많은 정점 (예 : 각 타일이 4 개의 하위 타일로 렌더링되는 경우)로 더 부드러운 조명을 얻을 수 있지만 원하는 품질에 따라 노력할 가치가 없을 수 있습니다. 가장 간단한 방법으로 먼저 시도하십시오.


1
line-of sight tests to each vertex? 그것은 비쌀 것입니다.
ashes999

영리하게 최적화 할 수 있습니다. 2D 게임의 경우 엄격한 그리드에 대해 놀라운 수의 광선 테스트를 매우 빠르게 수행 할 수 있습니다. 직선 LOS 테스트 대신 광선 테스트를 수행하는 대신 정점에 대한 조명 값을 "흐르는"(현재 인생에서 적절한 용어를 생각할 수 없습니다. 맥주 밤) 수 있습니다.
Sean Middleditch 2013 년

시선 테스트를 사용하면이 문제가 더 복잡해집니다. 이미 조명을 알아 냈습니다. 이제 4 개의 정점을 셰이더에 보낼 때 어떻게해야합니까? 실제 코드 샘플을 제공하는 데 불편 함을 느끼지 않지만 조금 더 많은 정보를 얻을 수 있다면 좋을 것입니다. 또한 더 많은 정보로 질문을 편집했습니다.
Cyral
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.