找回密码
 注册帐号

扫一扫,访问微社区

士郎 基于实例渲染的可控草地

20
回复
2642
查看
[ 复制链接 ]
排名
1
昨日变化

7836

主题

8394

帖子

3万

积分

Rank: 16

UID
1231
好友
186
蛮牛币
11039
威望
30
注册时间
2013-7-29
在线时间
4019 小时
最后登录
2019-6-17

活力之星原创精华达人突出贡献奖财富之证游戏蛮牛QQ群会员蛮牛妹VIP

2018-12-28 14:15:07 显示全部楼层 阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?注册帐号

x
主要程序
[AppleScript] 纯文本查看 复制代码
using UnityEngine;
using UnityEngine.Rendering;

public class GrassManage : MonoBehaviour
{
	[Header("最大绘制数量")]
	public int maxCount = 100000;
	[Header("绘制模型")]
	public Mesh GrassMesh;
	[Header("绘制材质")]
	public Material GrassMaterial;
	private ComputeBuffer grassComputeBuffer;
	private uint[] args = new uint[5] {0, 0, 0, 0, 0};
	private ComputeBuffer argsComputeBuffer;
	private Bounds drawBounds = new Bounds(Vector3.zero, new Vector3(500.0f, 100.0f, 500.0f));	
	private MaterialPropertyBlock mpb;
	private Camera _camera;

	[Header("草数量")]
	public int grassCount = 10000;
	private GrassInfo[] grassArr;
	[Header("填充范围")]
	public float fillRange = 500;
	[Header("发射高度")]
	public float SendHeight = 500;

	[Header("中心偏移")] public Vector3 GrassCenter;
	
	[Header("脚印")] public Stamp stamp;
	[Header("压低程度")] [Range(0, 1)] public float stampMin = .1f;
	//private int groundLayerId;
	public struct GrassInfo
	{
		public Vector4 position;
	}
	void Start ()
	{
		init();
		//获得地面的层索引
		//groundLayerId = LayerMask.GetMask("ground");
	}


	
	void init()
	{
		_camera=Camera.main;
		mpb=new MaterialPropertyBlock();
		//GrassInfo 内存大小 float 4*4=16
		grassComputeBuffer=new ComputeBuffer(maxCount,16);
		//绘制参数
		argsComputeBuffer=new ComputeBuffer(1, args.Length * sizeof(uint), ComputeBufferType.IndirectArguments);
		
		args[0] = GrassMesh.GetIndexCount(0);
		args[1] = 0;
		args[2] = GrassMesh.GetIndexStart(0);
		args[3] = GrassMesh.GetBaseVertex(0);
	}

	void fillGrass()
	{
		grassArr=new GrassInfo[grassCount];
		float half = fillRange / 2;
		int i = grassCount-1;
		//防止死循环
		int maxCount = grassCount * 2;
		while(i>=0 && maxCount>0)
		{
			Vector3 p=new Vector3();
			p.x = Random.Range(-half, half);
			p.y = SendHeight;
			p.z = Random.Range(-half, half);
			p += GrassCenter;
			if (getGround(ref p))
			{
				grassArr[i].position = new Vector4(p.x,p.y,p.z,Random.Range(.5f,1f));
				i--;
			}
			maxCount--;
		}
		//更新位置信息
		grassComputeBuffer.SetData(grassArr);
		GrassMaterial.SetBuffer("positionBuffer", grassComputeBuffer);
		//更新参数
		args[1] = (uint) grassCount;
		argsComputeBuffer.SetData(args);
	}

	bool getGround(ref Vector3 p)
	{
		Ray ray = new Ray(p, Vector3.down);
		RaycastHit hit;
		//if (Physics.Raycast(ray, out hit, p.y, groundLayerId))
		if (Physics.Raycast(ray, out hit, p.y+10))
		{
			if (hit.collider.CompareTag("ground"))
			{
				//如果命中地面,则使用命中后的位置.
				p = hit.point;
				return true;
			}
		}

		return false;
	}
		
	void Update ()
	{
		drawGrass();
		if (Input.GetKeyDown(KeyCode.G))
		{
			fillGrass();
		}
	}


	void drawGrass()
	{
		if (GrassMesh == null || GrassMaterial == null) return;
		if (stamp != null)
		{
			GrassMaterial.SetVector("_StampVector",
				new Vector4(stamp.Center.x, stampMin, stamp.Center.z, stamp.Size));
		}
		/*Graphics.DrawMeshInstancedIndirect(
		 *模型数据
		 * 子模型索引
		 * 绘制材质
		 * 绘制包围盒,出包围盒会导致不绘制
		 * 绘制参数
		 * 参数偏移
		 * 材质属性块
		 * 产生投影
		 * 接受投影
		 * 绘制层
		 * 绘制摄像机
		 * )
		 */
		Graphics.DrawMeshInstancedIndirect(GrassMesh, 0, GrassMaterial, drawBounds, argsComputeBuffer, 0,
			mpb, ShadowCastingMode.Off, true, 0, _camera);
		
	}
}


踩踏投影
1.png
正交模式投影,跟随主摄像机移动,不可旋转.
深度设置-2,比主摄像机早渲染
只需要渲染需要压低草地的元素
2.png
可以建立跟随组件移动的面片,这样只需要渲染压低范围就可以了.动态的元素可以产生动态的效果.
渲染到纹理,传递到草地材质使用.
[AppleScript] 纯文本查看 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(Camera))]
public class Stamp : MonoBehaviour
{
	public Transform CameraTr;
	private Camera camera;
	private float height;
	public Material stampMat;
	public float Size = 100;

	public Vector3 Center
	{
		get { return transform.position; }
	}

	public RenderTexture GetTex()
	{
		
		return camera!=null?camera.targetTexture:null;
	}
	void Start ()
	{
		camera = GetComponent<Camera>();
		height = camera.farClipPlane * .9f;
		if (CameraTr == null) CameraTr = Camera.main.transform;
	}

	private void LateUpdate()
	{
		transform.position = CameraTr.position+new Vector3(0,500,0);
	}
	
}


草地shader
3.png
[AppleScript] 纯文本查看 复制代码
Shader "Cpz/Grass"
{
	Properties {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
		_WindTex ("风贴图", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
        [HDR]_Color ("颜色", Color) = (0,1,0,1)
        _Height("高度",Float)=1
        _WindSpeed("风速",Float)=2
        _WindSize("风尺寸",Float)=10
	_StampTex ("脚印贴图", 2D) = "white" {}
        _StampVector("脚印中心",Vector)=(0,0,0,0)
    }
    SubShader {
        Tags {"Queue"="AlphaTest" "RenderType"="Opaque" "IgnoreProjector"="True"}
        LOD 200
        Cull Off

        CGPROGRAM
        #pragma multi_compile_instancing
        #pragma surface surf Standard Custom addshadow vertex:vert
        
        #pragma instancing_options procedural:setup
        #include "UnityPBSLighting.cginc"
        
        sampler2D _MainTex;
        sampler2D _WindTex;
        float _Height;
        float _WindSpeed;
        float _WindSize;
        float4 _StampVector;
        sampler2D _StampTex;
        
        half _Glossiness;
        half _Metallic;
        float4 _Color;
        struct Input {
            float2 uv_MainTex;
        };

        #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
            StructuredBuffer<float4> positionBuffer;
        #endif

        void rotate2D(inout float2 v, float r)
        {
            float s, c;
            sincos(r, s, c);
            v = float2(v.x * c - v.y * s, v.x * s + v.y * c);
        }
        void setup()
        {
        #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
            float4 data = positionBuffer[unity_InstanceID];
            float scale=_Height*data.w;
            unity_ObjectToWorld._11_21_31_41 = float4(scale, 0, 0, 0);
            unity_ObjectToWorld._12_22_32_42 = float4(0, scale, 0, 0);
            unity_ObjectToWorld._13_23_33_43 = float4(0, 0, scale, 0);
            unity_ObjectToWorld._14_24_34_44 = float4(data.xyz, 1);
            unity_WorldToObject = unity_ObjectToWorld;
            unity_WorldToObject._14_24_34 *= -1;
            unity_WorldToObject._11_22_33 = 1.0f / unity_WorldToObject._11_22_33;
        #endif
        }
        void RotateFixed(inout appdata_full v,float rote){
            float2 rv2=float2(v.vertex.x,v.vertex.z);
            rotate2D(rv2,rote);
            v.vertex.x=rv2.x;
            v.vertex.z=rv2.y;
        }
        float GetStamp(float2 position,float height){
            //_StampVector.xz   踩踏投影的水平坐标点
            //_StampVector.y    最低降低程度
            //_StampVector.w    踩踏投影尺寸
            //以物体减去踩踏投影中心点的的位置采样踩踏数据
            //超出范围不采样
            float2 stampUv=(position.xy-_StampVector.xz)/_StampVector.w+float2(0.5,.5);
            float4 stampP=float4(0,0,0,0);
            if(stampUv.x>0 && stampUv.x<1 && stampUv.y>0 && stampUv.y<1){
                stampP=tex2Dlod(_StampTex,float4(stampUv,0.0,0.0));
            }             
            float y=height*(1-stampP.a);
            return min(height,max(_StampVector.y,y));
        }  
        
        float GetWindWave(float2 position,float height){
            //以物体坐标点采样风的强度,
            //风按照时间*风速移动,以高度不同获得略微有差异的数据
            //移动值以高度不同进行减免,越低移动的越少.
            //根据y值获得不同的
            float4 p=tex2Dlod(_WindTex,float4(position/_WindSize+float2(_Time.x*_WindSpeed+height*.01,0),0.0,0.0)); 
            return (height*(p.r-.5));
        } 
        //自定义的顶点运算
        void vert (inout appdata_full v, out Input o) {
            UNITY_INITIALIZE_OUTPUT(Input,o);
            #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
            float4 data = positionBuffer[unity_InstanceID];
            //对植物按照固态数据进行旋转 
            RotateFixed(v,(data.x+data.y+data.z+data.w*10000));
            //获取踩踏数据
            v.vertex.y=GetStamp(data.xz,v.vertex.y);
            //获取风流动数据
            float w=GetWindWave(data.xz,v.vertex.y);
            //水平推动草的位置
            v.vertex.x+=w;
            //压低草的高度            
            v.vertex.y+=w*.4;            
            #endif
        }
        
        void surf (Input IN, inout SurfaceOutputStandard o) {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
            o.Albedo = c.rgb*_Color;            
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;    
            clip(c.a-0.6);            
        }
       
        ENDCG
    }
    //FallBack "Diffuse"
}

4.png
给地面组件添加 ground tag ,避免在不应该的地方生成草.
可以加图层过滤进行射线检测.
5.png
使用交叉的面片,靠上部增加顶点,带来一些因为高度不同的位移,让草的摇动柔软一些,并把uv放在一个面上,不用特意的去制作贴图,虽然效果会差一些.
6.png

设置参数.
运行,G键生成草地
工程文件:点击此处
知乎@草皮子

参与人数 1鲜花 +1 收起 理由
Xunustar + 1 很给力!

查看全部评分总评分 : 鲜花 +1

回复

使用道具 举报

7日久生情
2288/5000
排名
1393
昨日变化

0

主题

709

帖子

2288

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
135463
好友
0
蛮牛币
35
威望
0
注册时间
2016-1-23
在线时间
689 小时
最后登录
2019-6-18
2018-12-28 16:45:31 显示全部楼层
非常好,非常干活,666666666666
回复 支持 反对

使用道具 举报

6蛮牛粉丝
1379/1500
排名
1980
昨日变化

0

主题

270

帖子

1379

积分

Rank: 6Rank: 6Rank: 6

UID
68040
好友
0
蛮牛币
2001
威望
0
注册时间
2015-1-13
在线时间
425 小时
最后登录
2019-6-17
2018-12-28 19:03:10 显示全部楼层
好干好干
回复

使用道具 举报

5熟悉之中
897/1000
排名
5173
昨日变化

1

主题

302

帖子

897

积分

Rank: 5Rank: 5

UID
258102
好友
1
蛮牛币
1236
威望
0
注册时间
2017-12-6
在线时间
316 小时
最后登录
2019-6-18
2018-12-29 08:49:54 显示全部楼层
学到了
回复

使用道具 举报

7日久生情
1690/5000
排名
1664
昨日变化

0

主题

405

帖子

1690

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
136635
好友
0
蛮牛币
1768
威望
0
注册时间
2016-2-15
在线时间
506 小时
最后登录
2019-2-19
2018-12-29 09:22:41 显示全部楼层
可以很好,谢谢
回复

使用道具 举报

5熟悉之中
718/1000
排名
5234
昨日变化

2

主题

202

帖子

718

积分

Rank: 5Rank: 5

UID
208267
好友
0
蛮牛币
1531
威望
0
注册时间
2017-2-24
在线时间
240 小时
最后登录
2019-6-17
2018-12-29 10:00:17 显示全部楼层
有点东西
回复

使用道具 举报

6蛮牛粉丝
1283/1500
排名
3640
昨日变化

2

主题

586

帖子

1283

积分

Rank: 6Rank: 6Rank: 6

UID
112390
好友
2
蛮牛币
1527
威望
0
注册时间
2015-7-7
在线时间
283 小时
最后登录
2019-6-17
2018-12-29 11:19:12 显示全部楼层
不错,不错,谢谢分享
回复 支持 反对

使用道具 举报

7日久生情
1714/5000
排名
2263
昨日变化

0

主题

643

帖子

1714

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
182268
好友
2
蛮牛币
758
威望
0
注册时间
2016-11-9
在线时间
450 小时
最后登录
2019-4-22
2018-12-30 12:33:39 显示全部楼层
感谢分享感谢分享感谢分享感谢分享感谢分享感谢分享感谢分享感谢分享感谢分享感谢分享感谢分享感谢分享感谢分享感谢分享
回复 支持 反对

使用道具 举报

5熟悉之中
820/1000
排名
10817
昨日变化

3

主题

585

帖子

820

积分

Rank: 5Rank: 5

UID
310426
好友
0
蛮牛币
916
威望
0
注册时间
2019-1-2
在线时间
136 小时
最后登录
2019-6-18
2019-1-2 11:24:00 显示全部楼层
这种分享很给力,赞!
回复 支持 反对

使用道具 举报

5熟悉之中
789/1000
排名
3834
昨日变化

1

主题

178

帖子

789

积分

Rank: 5Rank: 5

UID
225051
好友
0
蛮牛币
811
威望
0
注册时间
2017-6-4
在线时间
218 小时
最后登录
2019-6-18
2019-1-4 08:15:51 显示全部楼层
研究的真好,膜拜
回复

使用道具 举报

8常驻蛮牛
5265/10000
排名
1669
昨日变化

0

主题

3763

帖子

5265

积分

Rank: 8Rank: 8

UID
185339
好友
1
蛮牛币
3589
威望
0
注册时间
2016-11-20
在线时间
724 小时
最后登录
2019-6-17
2019-1-4 09:01:50 显示全部楼层
{:91:}
回复

使用道具 举报

7日久生情
1632/5000
排名
1184
昨日变化

1

主题

105

帖子

1632

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
121885
好友
0
蛮牛币
2264
威望
0
注册时间
2015-9-9
在线时间
542 小时
最后登录
2019-6-12
QQ
2019-1-4 09:59:21 显示全部楼层
我就看看不说话
回复

使用道具 举报

4四处流浪
312/500
排名
7799
昨日变化

3

主题

68

帖子

312

积分

Rank: 4

UID
254117
好友
4
蛮牛币
1740
威望
0
注册时间
2017-11-13
在线时间
91 小时
最后登录
2019-3-15
2019-1-4 10:36:48 显示全部楼层
干货满满,谢谢!!!
回复

使用道具 举报

7日久生情
2117/5000
排名
2353
昨日变化

8

主题

781

帖子

2117

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
40014
好友
16
蛮牛币
2880
威望
0
注册时间
2014-8-15
在线时间
554 小时
最后登录
2019-6-18
QQ
2019-1-6 06:42:11 显示全部楼层
基于实例渲染的可控草地
回复 支持 反对

使用道具 举报

3偶尔光临
168/300
排名
14497
昨日变化

0

主题

84

帖子

168

积分

Rank: 3Rank: 3Rank: 3

UID
307176
好友
0
蛮牛币
147
威望
0
注册时间
2018-12-5
在线时间
28 小时
最后登录
2019-5-18
2019-1-6 12:21:23 显示全部楼层
可以可以 以为只能通过笔刷刷出草地 还有这种操作
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册帐号

本版积分规则