docs.unity3d.com
    目次を表示する/隠す

    フィルタースタック、フィルター、プロシージャルマスク

    続いて、フィルタースタックを Terrain (地形) ツールに追加します。フィルタースタックを使用すると、ツールの入力や出力に影響するプロシージャルマスクを生成できます。例えば、Slope フィルターを含むフィルタースタックを Smooth ツールに追加すると、Terrain のハイトマップのうち、Slope フィルターで指定した勾配範囲内の領域のみが滑らかになります。

    using UnityEngine;
    using UnityEditor;
    using UnityEditor.TerrainTools;
    
    internal class CustomTerrainToolWithMaskFilters : TerrainPaintTool<CustomTerrainToolWithMaskFilters>
    {
        private float m_BrushOpacity;
        private float m_BrushSize;
        private float m_BrushRotation;
    
        Material m_Material;
        Material material
        {
            get
            {
                if(m_Material != null) return m_Material;
    
                m_Material = new Material(Shader.Find("TerrainTool/BrushMaskFilterExample"));
                return m_Material;
            }
        }
    
        // FilterStack を作成します
        FilterStack m_FilterStack;
        FilterStack filterStack
        {
            get
            {
                if(m_FilterStack != null) return m_FilterStack;
    
                m_FilterStack = ScriptableObject.CreateInstance<FilterStack>();
                return m_FilterStack;
            }
        }
    
        // FilterStack の UI のビューを作成します
        FilterStackView m_FilterStackView;
        FilterStackView filterStackView
        {
            get
            {
                if(m_FilterStackView != null && m_FilterStackView.serializedFilterStack.targetObject != null)
                    return m_FilterStackView;
    
                m_FilterStackView = new FilterStackView(new GUIContent("Brush Mask Filters"), new SerializedObject( filterStack ) );
                m_FilterStackView.FilterContext = filterContext;
    
                return m_FilterStackView;
            }
        }
    
        // FilterContext を作成します。FilterStack のフィルターで使用されるプロパティバッグの一種です
        FilterContext m_FilterContext;
        private FilterContext filterContext
        {
            get
            {
                if (m_FilterContext != null) return m_FilterContext;
    
                m_FilterContext = new FilterContext(FilterUtility.defaultFormat, Vector3.zero, 1f, 0f);
                return m_FilterContext;
            }
        }
    
        public override string GetName()
        {
            return "Examples/Custom Terrain Tool With Mask Filters";
        }
    
        public override string GetDesc()
        {
            return "My custom Terrain Tool is amazing!";
        }
    
        public override void OnInspectorGUI(Terrain terrain, IOnInspectorGUI editContext)
        {
            editContext.ShowBrushesGUI(5, BrushGUIEditFlags.Select);
            m_BrushOpacity = EditorGUILayout.Slider("Opacity", m_BrushOpacity, 0, 1);
            m_BrushSize = EditorGUILayout.Slider("Size", m_BrushSize, .001f, 100f);
            m_BrushRotation = EditorGUILayout.Slider("Rotation", m_BrushRotation, 0, 360);
    
            // FilterStack の UI をレンダリングします。これにより、Terrain ツールの UI を介してフィルターを追加、削除できるようになります
            filterStackView.OnGUI();
        }
    
        void BlitFilterStackTexture(Terrain terrain, RenderTexture source, RenderTexture dest, Vector3 brushPos)
        {
            // FilterContext を準備します
            filterContext.brushPos = brushPos;
            filterContext.brushSize = m_BrushSize;
            filterContext.brushRotation = m_BrushRotation;
    
            using(new ActiveRenderTextureScope(null))
            {
                // FilterStack のフィルターで使用される可能性がある必須プロパティをバインドします。Terrain ツールの一部のフィルターは、くぼみや勾配などの Terrain のサイズに依存します
                TerrainData terrainData = terrain.terrainData;
                filterContext.floatProperties[FilterContext.Keywords.TerrainScale] = Mathf.Sqrt(terrainData.size.x * terrainData.size.x + terrainData.size.z * terrainData.size.z);
                filterContext.vectorProperties["_TerrainSize"] = new Vector4(terrainData.size.x, terrainData.size.y, terrainData.size.z, 0.0f);
    
                // FilterStack のフィルターで使用される可能性がある Terrain のテクスチャデータをバインドします
                filterContext.rtHandleCollection.AddRTHandle(0, FilterContext.Keywords.Heightmap, source.graphicsFormat);
                filterContext.rtHandleCollection.GatherRTHandles(source.width, source.height);
                Graphics.Blit(source, filterContext.rtHandleCollection[FilterContext.Keywords.Heightmap]);
                filterStack.Eval(filterContext, source, dest);
            }
    
            filterContext.ReleaseRTHandles();
        }
    
        private void RenderIntoPaintContext(Terrain terrain, UnityEngine.TerrainTools.PaintContext paintContext, Texture brushTexture, UnityEngine.TerrainTools.BrushTransform brushXform, Vector3 brushPos)
        {
            Material mat = material;
    
            // 出力の FilterStack RenderTexture を取得します       
            RTHandle filterTexture = RTUtils.GetTempHandle(paintContext.sourceRenderTexture.width, paintContext.sourceRenderTexture.height, 0, FilterUtility.defaultFormat);
            BlitFilterStackTexture(terrain, paintContext.sourceRenderTexture, filterTexture, brushPos);
            // FilterStack RenderTexture をツールのマテリアルにバインドします
            mat.SetTexture("_FilterTex", filterTexture);
    
            mat.SetTexture("_BrushTex", brushTexture);
            var opacity = Event.current.control ? -m_BrushOpacity : m_BrushOpacity;
            mat.SetVector("_BrushParams", new Vector4(opacity, 0.0f, 0.0f, 0.0f));
            UnityEngine.TerrainTools.TerrainPaintUtility.SetupTerrainToolMaterialProperties(paintContext, brushXform, mat);
            Graphics.Blit(paintContext.sourceRenderTexture, paintContext.destinationRenderTexture, mat, (int)UnityEngine.TerrainTools.TerrainPaintUtility.BuiltinPaintMaterialPasses.RaiseLowerHeight);
    
            // FilterStack の RenderTexture をリリースします
            RTUtils.Release(filterTexture);
        }
    
        public override void OnRenderBrushPreview(Terrain terrain, IOnSceneGUI editContext)
        {
            if (Event.current.type != EventType.Repaint) return;
            if (!editContext.hitValidTerrain) return;
    
            UnityEngine.TerrainTools.BrushTransform brushXform = UnityEngine.TerrainTools.TerrainPaintUtility.CalculateBrushTransform(terrain, editContext.raycastHit.textureCoord, m_BrushSize, m_BrushRotation);
            UnityEngine.TerrainTools.PaintContext paintContext = UnityEngine.TerrainTools.TerrainPaintUtility.BeginPaintHeightmap(terrain, brushXform.GetBrushXYBounds(), 1);
            Material previewMaterial = TerrainPaintUtilityEditor.GetDefaultBrushPreviewMaterial();
    
            // ブラシのプレビューのレンダリングを続行します
            TerrainPaintUtilityEditor.BrushPreview previewTexture = TerrainPaintUtilityEditor.BrushPreview.SourceRenderTexture;
            TerrainPaintUtilityEditor.DrawBrushPreview(paintContext, previewTexture, editContext.brushTexture, brushXform, previewMaterial, 0);
            RenderIntoPaintContext(terrain, paintContext, editContext.brushTexture, brushXform, editContext.raycastHit.point);
            RenderTexture.active = paintContext.oldRenderTexture;
            previewMaterial.SetTexture("_HeightmapOrig", paintContext.sourceRenderTexture);
            previewTexture = TerrainPaintUtilityEditor.BrushPreview.DestinationRenderTexture;
            TerrainPaintUtilityEditor.DrawBrushPreview(paintContext, previewTexture, editContext.brushTexture, brushXform, previewMaterial, 1);
            UnityEngine.TerrainTools.TerrainPaintUtility.ReleaseContextResources(paintContext);
        }
    
        public override bool OnPaint(Terrain terrain, IOnPaint editContext)
        {
            UnityEngine.TerrainTools.BrushTransform brushXform = UnityEngine.TerrainTools.TerrainPaintUtility.CalculateBrushTransform(terrain, editContext.uv, m_BrushSize, m_BrushRotation);
            UnityEngine.TerrainTools.PaintContext paintContext = UnityEngine.TerrainTools.TerrainPaintUtility.BeginPaintHeightmap(terrain, brushXform.GetBrushXYBounds());
    
            RenderIntoPaintContext(terrain, paintContext, editContext.brushTexture, brushXform, editContext.raycastHit.point);
            UnityEngine.TerrainTools.TerrainPaintUtility.EndPaintHeightmap(paintContext, "Terrain Paint - Raise or Lower Height");
    
            return true;
        }
    }
    ``
    
    Here is the Shader for the tool that is using a procedural texture from the FilterStack:
    
    ``
    Shader "TerrainTool/BrushMaskFilterExample"
    {
        Properties { _MainTex ("Texture", any) = "" {} }
    
        SubShader
        {
            ZTest Always Cull Off ZWrite Off
    
            HLSLINCLUDE
    
            #include "UnityCG.cginc"
            #include "Packages/com.unity.terrain-tools/Shaders/TerrainTools.hlsl"
    
            sampler2D _MainTex;
            float4 _MainTex_TexelSize;      // 1/width、1/height、width、height
    
            sampler2D _BrushTex;
            sampler2D _FilterTex;
    
            float4 _BrushParams;
            #define BRUSH_STRENGTH      (_BrushParams[0])
            #define BRUSH_TARGETHEIGHT  (_BrushParams[1])
            #define kMaxHeight          (32766.0f/65535.0f)
    
            struct appdata_t
            {
                float4 vertex : POSITION;
                float2 pcUV : TEXCOORD0;
            };
    
            struct v2f
            {
                float4 vertex : SV_POSITION;
                float2 pcUV : TEXCOORD0;
            };
    
            v2f vert(appdata_t v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.pcUV = v.pcUV;
                return o;
            }
    
            ENDHLSL
    
            Pass
            {
                Name "CustomTerrainTool"
    
                HLSLPROGRAM
    
                #pragma vertex vert
                #pragma fragment frag
    
                float4 frag(v2f i) : SV_Target
                {
                    float2 brushUV = PaintContextUVToBrushUV(i.pcUV);
                    // 境界外の乗数
                    float oob = all(saturate(brushUV) == brushUV) ? 1.0f : 0.0f;
    
                    // ソースのハイトマップテクスチャの領域となる MainTex をサンプリングして、指定された UV での現在の高さの値を取得します
                    // ここでは UnpackHeightmap が必要になります。UnpackHeightmap は、現在のプラットフォームやグラフィックスデバイスが R16_UNorm テクスチャフォーマットに対応していない場合に、高さの値を R および G チャンネルからアンパックするためです。R16_UNorm フォーマットに対応している場合は、R チャンネルのみから読み取りを行います
                    float height = UnpackHeightmap(tex2D(_MainTex, i.pcUV));
                    float filter = UnpackHeightmap(tex2D(_FilterTex, i.pcUV));
                    float brush = UnpackHeightmap(tex2D(_BrushTex, brushUV));
                    // 合成されたマスクの影響度を計算します
                    float brushShape = oob * brush * filter;
                    height = height + BRUSH_STRENGTH * brushShape;
    
                    // 書き込み先の RenderTexture に新しい高さを格納します。ハイトマップ自体は符号付きですが、Terrain のレンダリング時に符号なしのテクスチャとして処理されるため、0.0f と 0.5f の間で固定します
                    // ここでは PackHeightmap が必要です。PackHeightmap は、現在のプラットフォームやグラフィックスデバイスが R16_UNorm テクスチャフォーマットに対応していない場合に、高さの値を R および G チャンネルにパックするためです。R16_UNorm フォーマットに対応している場合、R チャンネルのみに書き込みを行います
                    return PackHeightmap(clamp(height, 0, kMaxHeight));
                }
    
                ENDHLSL
            }
        }
    }
    
    トップに戻る
    Copyright © 2023 Unity Technologies — 商標と利用規約
    • 法律関連
    • プライバシーポリシー
    • クッキー
    • 私の個人情報を販売または共有しない
    • Your Privacy Choices (Cookie Settings)