シェーダ内でスケール値を取得する方法をメモメモ…
※このページの内容の動作確認にはUnity2018.2を使用しています。
シェーダ内でオブジェクトのスケールを利用したいときってありますよね。スクリプト側からシェーダに値を渡すようにするのが一番わかりやすくて正確だと思いますが、どうしても「シェーダだけで完結させたい」という場合には次の方法が便利です。float scaleX = length(float3(unity_ObjectToWorld[0].x, unity_ObjectToWorld[1].x, unity_ObjectToWorld[2].x)); float scaleY = length(float3(unity_ObjectToWorld[0].y, unity_ObjectToWorld[1].y, unity_ObjectToWorld[2].y)); float scaleZ = length(float3(unity_ObjectToWorld[0].z, unity_ObjectToWorld[1].z, unity_ObjectToWorld[2].z));
組み込みのモデル行列 unity_ObjectToWorld からスケール値だけを取り出す方法です。注意点としては、ベクトルの長さを計算しているためパフォーマンス的に若干コストがかかること、精度誤差が生じること、メッシュデータのインポート設定によっては transform.lossyScale の値と一致しない場合があることなどが挙げられます。乱用はせず状況を見ながら使いましょう。
これを利用すれば、テクスチャの自動タイリング機能なんかは割と簡単に実装できます。
Shader "Custom/Auto-Tiling" { Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} _Glossiness ("Smoothness", Range(0,1)) = 0.5 _Metallic ("Metallic", Range(0,1)) = 0.0 } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf Standard fullforwardshadows vertex:vert #pragma target 3.0 sampler2D _MainTex; struct Input { float2 uv_MainTex; float2 scaleXY; }; void vert(inout appdata_full v, out Input o) { UNITY_INITIALIZE_OUTPUT(Input, o); // スケールを取得する float scaleX = length(float3(unity_ObjectToWorld[0].x, unity_ObjectToWorld[1].x, unity_ObjectToWorld[2].x)); float scaleY = length(float3(unity_ObjectToWorld[0].y, unity_ObjectToWorld[1].y, unity_ObjectToWorld[2].y)); o.scaleXY = float2(scaleX, scaleY); } half _Glossiness; half _Metallic; fixed4 _Color; void surf (Input IN, inout SurfaceOutputStandard o) { // スケールに合わせてUVを調整する fixed4 c = tex2D (_MainTex, IN.uv_MainTex * IN.scaleXY) * _Color; o.Albedo = c.rgb; o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }
まあ、わざわざスケール値を計算しなくてもワールド座標で事足りる場合がほとんどだとは思いますが…
参考
Can I get the scale in the transform of the object I attach a shader to? If so, how? - Unity Forum
知っていると便利?ワールド変換行列から情報を抜き出そう
0 件のコメント:
コメントを投稿