Unity3D用のGPUベースの2D照明システム





みなさんこんにちは。 ご存知のように、Unity3Dには2Dゲームの照明サポートがありません。 このようなシステムはAsset Storeで見つけることができますが、 1つの欠点があります-CPU上で実行され、多くのリソースを消費します(各光源のフレームごとに64-4096 reykast)。 そのため、私は自分の照明を作ることにしました。その性能はモバイル機器に十分なものになるでしょう。 このため、計算はGPUに転送されました。 テラリアやスターバウンドの光のようなものになりました。



デモへのリンク。 矢印-移動、スペースバー-シャーシ、R-再起動。 それから取られたスクリーンショット。



すべての照明は小さなテクスチャでカウントされ、この例では160x88ピクセルが使用されます。 解像度を上げると、非常に細かいグリッドを実現できますが、これはモバイルプラットフォーム用ではありませんが、気づきにくいものです。 このような小さなテクスチャで計算が実行されるため、かなり重いシェーダーを使用できます。



照明の操作には、3つのカメラが使用されます。各カメラは、システムの一部(光源、光の障害物、環境光)を担当します。 次に、光源と環境光が混合され、ゲームカメラに重ねられます。



次に、描画順序でさらに詳しく説明します。



光の障害





光閉塞テクスチャ。 RGBチャンネル。 これと類似のテクスチャのスケールは400%です



これは、カメラが与えるテクスチャです。 黒い領域は完全に透明で、白い領域は完全に不透明です。 色付きの領域もサポートされています。たとえば、完全に赤いピクセルが光の赤い部分をブロックし、緑と青を通過させます。



環境照明





環境光源





環境の光源。 アルファチャンネル





繰り返し生成される環境光テクスチャ





通常の光源がない、わずかに増幅された周囲光のように見えます



それはすべてもう少し複雑です。 光源のない空間にかすかな光を加えるために、このタイプの照明を実装しました。 例では、それを使用して、空き領域全体の薄暗い強調表示が実装されています。 RGBチャンネルは色を制御し、アルファチャンネルは光度を制御します。 このタイプの光源と従来の光源の主な違いは、反復光源と見なされ、方向性がないことです。



1ピクセルの計算アルゴリズム:



  1. 前の反復テクスチャから初期ピクセル値を取得します。
  2. 障害物のテクスチャからピクセルから障害物の力を引きます。
  3. 環境の光源のテクスチャからの光度をピクセルに追加します。
  4. 隣接するピクセルの平均値をピクセルに追加します




光源





光源



従来の光源は照明システムの主要部分です。 スプライトに似たものを使用して描画します。 必要に応じてポイントを任意の場所に移動できますが、すべての色は中心から来ます。

光源の場合、パスのトレースを含むシェーダーとパスのないシェーダーを使用できます。 トレースシェーダーは、トレースされるポイントの数が異なります。 これらのうち2つを使用します。1つは9ポイントで、Shader Model 2.0で動作し、もう1つは20ポイントでShader Model 3.0で動作します。 パストレースを使用しないシェーダーは、追加の情報を必要としないため、パーティクルシステムに使用されます。



パストレースアルゴリズム:



  1. テクスチャからピクセルの明るさを取得します。
  2. 光源の位置と障害物テクスチャの現在のピクセルを見つけます。
  3. 前のステップの2つのポイントの間にある障害物のピクセル値だけ現在の輝度を下げます。


9ポイントトレースシェーダー
Shader "Light2D/Light 9 Points" { Properties { _MainTex ("Light texture", 2D) = "white" {} _ObstacleMul ("Obstacle Mul", Float) = 500 _EmissionColorMul ("Emission color mul", Float) = 1 } SubShader { Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"} LOD 100 Blend OneMinusDstColor One Cull Off ZWrite Off Lighting Off Pass { CGPROGRAM // Upgrade NOTE: excluded shader from DX11 and Xbox360; has structs without semantics (struct v2f members sp) #pragma exclude_renderers d3d11 xbox360 #pragma vertex vert #pragma fragment frag #pragma glsl_no_auto_normalization #include "UnityCG.cginc" struct appdata_t { float4 vertex : POSITION; float2 texcoord : TEXCOORD0; fixed4 color : COLOR0; fixed4 normal : TEXCOORD1; }; struct v2f { float4 vertex : SV_POSITION; half2 texcoord : TEXCOORD0; fixed4 color : COLOR0; half4 scrPos : TEXCOORD2; half4 scrPosCenter : TEXCOORD1; }; sampler2D _ObstacleTex; sampler2D _MainTex; half _ObstacleMul; half _EmissionColorMul; v2f vert (appdata_t v) { v2f o; o.vertex = mul(UNITY_MATRIX_MVP, v.vertex); o.texcoord = v.texcoord; o.scrPos = ComputeScreenPos(o.vertex); o.scrPosCenter = v.normal; o.color = v.color; return o; } fixed3 maximize(fixed3 vec){ vec = max(vec, fixed3(0.01, 0.01, 0.01)); return vec/max(vec.x, max(vec.y, vec.z)); } half sum(half3 vec){ return vec.x + vec.y + vec.z; } fixed4 frag (v2f i) : COLOR { fixed2 thisPos = (i.scrPos.xy/i.scrPos.w); fixed2 centerPos = i.scrPosCenter; const fixed sub = 0.111111111111; fixed m = _ObstacleMul*length((thisPos - centerPos)*fixed2(_ScreenParams.x/_ScreenParams.y, 1)*sub); fixed4 tex = tex2D(_MainTex, i.texcoord); clip(tex.a - 0.005); fixed4 col = i.color*fixed4(tex.rgb, 1)*tex.a; fixed pos = 1; pos -= sub; col *= saturate(1 - tex2D(_ObstacleTex, lerp(centerPos, thisPos, pos))*m); pos -= sub; col *= saturate(1 - tex2D(_ObstacleTex, lerp(centerPos, thisPos, pos))*m); pos -= sub; col *= saturate(1 - tex2D(_ObstacleTex, lerp(centerPos, thisPos, pos))*m); pos -= sub; col *= saturate(1 - tex2D(_ObstacleTex, lerp(centerPos, thisPos, pos))*m); pos -= sub; col *= saturate(1 - tex2D(_ObstacleTex, lerp(centerPos, thisPos, pos))*m); pos -= sub; col *= saturate(1 - tex2D(_ObstacleTex, lerp(centerPos, thisPos, pos))*m); pos -= sub; col *= saturate(1 - tex2D(_ObstacleTex, lerp(centerPos, thisPos, pos))*m); pos -= sub; col *= saturate(1 - tex2D(_ObstacleTex, lerp(centerPos, thisPos, pos))*m); pos -= sub; col *= saturate(1 - tex2D(_ObstacleTex, lerp(centerPos, thisPos, pos))*m); col.rgb *= _EmissionColorMul; return col; } ENDCG } } }
      
      









光のブレンドとブレンド





光源+環境光



ソースのライトと環境のライトがレンダリングされた後、それらを互いに混合できます。 これを行うには、テクスチャにアルファを掛けて追加します。 次に、これらすべてがゲームの画像にスーパーインポーズされ、画面に表示されます。





結果のスクリーンショット、クリック時の高解像度。



そして最後に、賛否両論



長所:



短所:





PSコミュニティの関心がある場合、私はそれを完成させてアセットストアに掲載します。

PPSが投稿されました。 ここにリンクがあります

PPPSが無料でオープンソースになりました。 Github



All Articles