HSVでグリッドな床シェーダーを作ってみた
かなり久しぶりの投稿です...!
今回は, 先月の学内プロコン用に作ったシェーダーを作った際の工夫を書いてきます!
今年のプロコンに出したプロジェクト
— Yuz (@Eddie_Le_Garden) 2019年11月3日
モーキャプで積み木ゲーム#unity pic.twitter.com/TEh0bxTf3q
イメージとしてはplaneオブジェクトにグリッド線の描画し, さらにBloomエフェクトを設定して色相を動的に変更していく感じです
※本シェーダーではPost Processing Stack を必要とします
Surfaceシェーダ作成
実際に書いたシェーダーが下記になります↓
Shader "Custom/HsvGrid" { Properties{ _GridThickness("Grid Thickness", Float) = 0.1 _GridSpacing("Grid Spacing", Float) = 9.0 _Freq("frequency", Range(1,3)) = 1 _Bloom("Bloom", float) = 7 } SubShader{ Tags{ "Render" = "Opaque" } LOD 200 CGPROGRAM #pragma surface surf Standard fullforwardshadows #pragma target 3.0 struct Input { float3 worldPos; }; //HSB→RGB fixed3 hsv2rgb(fixed3 c) { fixed4 K = fixed4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); fixed3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); return c.z * lerp(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); } float _GridThickness; //グリッド線の太さ float _GridSpacing; //グリッドの間隔 float _Freq; //色相が変化する早さ float _Bloom; //Bloomエフェクト用の係数 void surf(Input IN, inout SurfaceOutputStandard o) { float _Hue = _Time * _Freq; //色相 //グリッド線 o.Albedo = (frac(IN.worldPos.x / _GridSpacing) < _GridThickness || frac(IN.worldPos.z / _GridSpacing) < _GridThickness) ? hsv2rgb(fixed3(_Hue % 1, 1, 1))*_Bloom : fixed3(0, 0, 0); } ENDCG } FallBack "Diffuse" }
これをPlaneオブジェクトに適用します↓
グリッド線のジャギー対策
この時点だと上記のキャプチャから遠くのグリッド線にジャギーが生じてしまうことが確認できます
今回はこれを, むろ小径さん(@murosyoukei)のツイートを参考に改善していきます
シェーダーでグリッドを表現しようとすると遠くの方でジャギる現象を
— むろ小径 (@murosyoukei) 2019年10月8日
グリッドの太さを深度値で変化させるようにして解決する試み pic.twitter.com/Tc5EIk00e8
このツイートを基に改善したシェーダーがこちらです↓
Shader "Custom/HsvGrid" { Properties{ _GridThickness("Grid Thickness", Float) = 0.1 _GridSpacing("Grid Spacing", Float) = 9.0 _Freq("frequency", Range(1,3)) = 1 _Bloom("Bloom", float) = 7 _Center("Center", Vector) = (0,0,0,0) //変更点1 _Range("Renge", Float) = 0.05 //変更点2 } SubShader{ Tags{ "Render" = "Opaque" } LOD 200 CGPROGRAM #pragma surface surf Standard fullforwardshadows #pragma target 3.0 struct Input { float3 worldPos; }; //HSB→RGB fixed3 hsv2rgb(fixed3 c) { fixed4 K = fixed4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); fixed3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); return c.z * lerp(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); } float _GridThickness; float _GridSpacing; float _Freq; float _Bloom; float4 _Center; //変更点1 float _Range; //変更点2 void surf(Input IN, inout SurfaceOutputStandard o) { float _Hue = _Time * _Freq; //色相 float _DepthThick = (distance(fixed3(_Center.x, 0, _Center.z), IN.worldPos)*_Range); //変更点3 //グリッド線 o.Albedo = (frac(IN.worldPos.x / _GridSpacing) < _GridThickness + _DepthThick //変更点4 || frac(IN.worldPos.z / _GridSpacing) < _GridThickness + _DepthThick) ? hsv2rgb(fixed3(_Hue % 1, 1, 1))*_Bloom : fixed3(0, 0, 0); } ENDCG } FallBack "Diffuse" }
変更点1, 2ではそれぞれ, MainCameraの位置とその距離に応じてグリッド線を太くするための係数を示します
surf関数では実際にMainCameraとの距離を求め, _DepthThick (変更点3) に加算する太さを代入しています
各プロパティの値は大体こんな感じに設定しました↓
次に, 上記のシェーダーにMainCameraの座標を渡すスクリプトを作成します
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PosShader : MonoBehaviour { [SerializeField] Renderer mat; void Update() { mat.material.SetVector("_Center", transform.position); } }
このスクリプトをMainCameraにアタッチし, インスペクターから"mat"にPlaneオブジェクトを指定します.
これで実行すると大体こんな感じになるかと思います↓
以前のグリッドシェーダーにMainCameraの座標を渡すように改善しました!#Unity pic.twitter.com/c5CiumzYvq
— Yuz (@Eddie_Le_Garden) 2019年12月6日
右のGame画面からも視点が線に近づくにつれ細くなっていることが確認できると思います.
まとめ
完全にジャギーを消すにはさらに色々と改善しなければならない点はあると思いますが, とりあえず簡単な動きだけ実装しました
今後何かいい方法が思いついたらまた投稿したいと思います!
今回, むろ小径さんにはジャギーの改善以外にも色々お世話になりました
本当にありがとうございます...!
下記参考
hsva-unity/HSVRangeShader.shader at master · greggman/hsva-unity · GitHub