当前位置:网站首页>Unity analyzes the rendering of built-in terrain and does some interesting things
Unity analyzes the rendering of built-in terrain and does some interesting things
2022-06-10 20:57:00 【Luo Xiao C】
Paint grass while painting grass texture :
Shader "LSQ/Terrain Rendering/Custom Terrain/Grass Paint Terrain VFS"
{
Properties
{
[Enum(Splat0,0,Splat1,1,Splat2,2,Splat3,3)] _GrassLayerIndex("Grass Layer Index", int) = 1
_GrassTex("Grass Texture", 2D) = "white"{
}
_TopColor("Grass Top Color", Color) = (1,1,1,1)
_BottomColor("Grass Bottom Color", Color) = (1,1,1,1)
_Height("Grass Height", float) = 3
_Width("Grass Width", range(0, 1)) = 0.05
[Toggle]_FacingCamera("Facing Camera", int) = 1
[Toggle]_WindToggle("Use Wind", int) = 0
_WindStrength("Wind Strength", float) = 1
}
SubShader
{
Tags {
"RenderType" = "Opaque" }
Pass //Terrain
{
Tags {
"LightMode"="ForwardBase" }
CGPROGRAM
#pragma multi_compile_fwdbase
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float3 normal : NORMAL;
// Coordinates of shadow texture sampling ( The parameter is the stored texture channel )
SHADOW_COORDS(2)
};
v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
o.normal = UnityObjectToWorldNormal(v.normal);
// Calculate shadow texture coordinates
TRANSFER_SHADOW(o);
return o;
}
sampler2D _Control;
// Textures
sampler2D _Splat0, _Splat1, _Splat2, _Splat3;
float4 _Splat0_ST, _Splat1_ST, _Splat2_ST, _Splat3_ST;
fixed4 frag (v2f i) : SV_Target
{
fixed4 splatControl = tex2D(_Control, i.uv);
fixed4 col = splatControl.r * tex2D (_Splat0, i.uv * _Splat0_ST.xy);
col += splatControl.g * tex2D(_Splat1, i.uv * _Splat1_ST.xy);
col += splatControl.b * tex2D (_Splat2, i.uv * _Splat2_ST.xy);
col += splatControl.a * tex2D (_Splat3, i.uv * _Splat3_ST.xy);
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * col;
fixed3 lightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed3 diffuse = _LightColor0.rgb * saturate(dot(i.normal, lightDir)) * col;
return fixed4(ambient + diffuse * atten, 1.0);
}
ENDCG
}
Pass //Grass
{
Tags {
"LightMode"="ForwardBase" }
Cull OFF
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma geometry geom
#include "UnityCG.cginc"
#include "Lighting.cginc"
#pragma target 4.0
sampler2D _GrassTex;
fixed4 _TopColor;
fixed4 _BottomColor;
float _Height;
float _Width;
bool _FacingCamera;
bool _WindToggle;
float _WindStrength;
sampler2D _Control;
int _GrassLayerIndex;
struct v2g
{
float4 pos : SV_POSITION;
float3 norm : NORMAL;
float2 uv : TEXCOORD0;
};
struct g2f
{
float4 pos : SV_POSITION;
float3 norm : NORMAL;
float2 uv : TEXCOORD0;
};
v2g vert(appdata_full v)
{
v2g o;
o.pos = v.vertex;
o.norm = v.normal;
o.uv = v.texcoord;
return o;
}
float3 WindEffect(float3 root, float height, float random)
{
const float oscillateDelta = 0.05;
float2 wind = float2(sin(_Time.x * UNITY_PI * 5), sin(_Time.x * UNITY_PI * 5));
wind.x += (sin(_Time.x + root.x / 25) + sin((_Time.x + root.x / 15) + 50)) * 0.5;
wind.y += cos(_Time.x + root.z / 80);
wind *= lerp(0.7, 1.0, 1.0 - random);
float oscillationStrength = 2.5f;
float sinSkewCoeff = random;
float lerpCoeff = (sin(oscillationStrength * _Time.x + sinSkewCoeff) + 1.0) / 2;
float2 leftWindBound = wind * (1.0 - oscillateDelta);
float2 rightWindBound = wind * (1.0 + oscillateDelta);
wind = lerp(leftWindBound, rightWindBound, lerpCoeff);
float randomAngle = lerp(-UNITY_PI, UNITY_PI, random);
float randomMagnitude = lerp(0, 1., random);
float2 randomWindDir = float2(sin(randomAngle), cos(randomAngle));
wind += randomWindDir * randomMagnitude;
float windForce = length(wind);
root.xz += wind.xy * height * _WindStrength;
root.y -= windForce * height * 0.8 * _WindStrength;
return root;
}
g2f createGSOut()
{
g2f output;
output.pos = float4(0, 0, 0, 0);
output.norm = float3(0, 0, 0);
output.uv= float2(0, 0);
return output;
}
[maxvertexcount(30)]
void geom(point v2g points[1], inout TriangleStream<g2f> triStream)
{
float grassFade = tex2Dlod(_Control, fixed4(points[0].uv, 0, 0))[_GrassLayerIndex];
if (grassFade > 0.5)
{
const int vertexCount = 12;
g2f v[vertexCount] =
{
createGSOut(), createGSOut(), createGSOut(), createGSOut(),
createGSOut(), createGSOut(), createGSOut(), createGSOut(),
createGSOut(), createGSOut(), createGSOut(), createGSOut()
};
float3 root = points[0].pos.xyz;
float random = sin(UNITY_HALF_PI * frac(root.x) + UNITY_HALF_PI * frac(root.z));
float width = _Width + (random / 50);
float height = _Height + (random / 5);
height *= grassFade;
float currentV = 0;
float offsetV = 1.f / (vertexCount * 0.5 - 1);
float currentHeightOffset = 0;
float currentVertexHeight = 0;
float3 right = float3(1,0,0);
float3 up = float3(0,1,0);
if(_FacingCamera)
{
right = UNITY_MATRIX_IT_MV[0].xyz;
up = UNITY_MATRIX_IT_MV[1].xyz;
}
for (int i = 0; i < vertexCount; i++)
{
v[i].norm = fixed3(0, 1, 0);
if (_WindToggle)
{
root = WindEffect(root, currentV, random);
}
if (fmod(i, 2) == 0)
{
v[i].pos = float4(root - width * right + currentVertexHeight * up, 1);
v[i].uv = float2(0, currentV);
}
else
{
v[i].pos = float4(root + width * right + currentVertexHeight * up, 1);
v[i].uv = float2(1, currentV);
currentV += offsetV;
currentVertexHeight = currentV * height;
}
v[i].pos = UnityObjectToClipPos(v[i].pos);
}
for (int p = 0; p < (vertexCount - 2); p++)
{
triStream.Append(v[p]);
triStream.Append(v[p + 2]);
triStream.Append(v[p + 1]);
}
}
}
half4 frag(g2f IN) : COLOR
{
fixed4 tex = tex2D(_GrassTex, IN.uv);
fixed4 color = tex * lerp(_BottomColor, _TopColor, IN.uv.y);
half3 worldNormal = UnityObjectToWorldNormal(IN.norm);
fixed3 ambient = ShadeSH9(half4(worldNormal, 1));
fixed3 diffuseLight = saturate(dot(worldNormal, UnityWorldSpaceLightDir(IN.pos))) * _LightColor0;
fixed3 halfVector = normalize(UnityWorldSpaceLightDir(IN.pos) + WorldSpaceViewDir(IN.pos));
fixed3 specularLight = pow(saturate(dot(worldNormal, halfVector)), 15) * _LightColor0;
fixed3 light = ambient + diffuseLight + specularLight;
clip(tex.a - 0.99);
return fixed4(color.rgb * light, 1);
}
ENDCG
}
}
}
Terrain editing at runtime :
using System;
using System.Collections;
using UnityEngine;
public class TerrainUtil
{
#region Terrain height brush
/// <summary>
/// elevated Terrain The height of a point on .
/// </summary>
/// <param name="terrain">Terrain</param>
/// <param name="point"> World coordinate point </param>
/// <param name="opacity"> Raised height </param>
/// <param name="size"> Brush size </param>
/// <param name="amass"> When the height of other points within the brush range is higher than the brush center point, whether to increase the height of other points at the same time </param>
public static void Rise(Terrain terrain, Vector3 point, float opacity, int size)
{
TerrainData tData = terrain.terrainData;
// Set the brush size
Vector2Int index = GetHeightmapIntIndex(terrain, point);
int bound = size / 2;
int xBase = index.x - bound >= 0 ? index.x - bound : 0;
int yBase = index.y - bound >= 0 ? index.y - bound : 0;
int width = xBase + size <= tData.heightmapResolution ? size : tData.heightmapResolution - xBase;
int height = yBase + size <= tData.heightmapResolution ? size : tData.heightmapResolution - yBase;
// Get the height map within the brush range and normalize the height to [0, 1]
float[,] heights = tData.GetHeights(xBase, yBase, width, height);
float initHeight = tData.GetHeight(index.x, index.y) / tData.size.y;
float deltaHeight = opacity / tData.size.y;
// Apply brush
ApplyBrush(heights, deltaHeight, initHeight, height, width);
tData.SetHeights(xBase, yBase, heights);
}
/// <summary>
/// Reduce Terrain The height of a point on .
/// </summary>
/// <param name="terrain">Terrain</param>
/// <param name="point"> World coordinate point </param>
/// <param name="opacity"> Lowered height </param>
/// <param name="size"> Brush size </param>
/// <param name="amass"> When the height of other points within the brush range is lower than the brush center point, whether to reduce the height of other points at the same time </param>
public static void Sink(Terrain terrain, Vector3 point, float opacity, int size)
{
TerrainData tData = terrain.terrainData;
// Set the brush size
Vector2Int index = GetHeightmapIntIndex(terrain, point);
int bound = size / 2;
int xBase = index.x - bound >= 0 ? index.x - bound : 0;
int yBase = index.y - bound >= 0 ? index.y - bound : 0;
int width = xBase + size <= tData.heightmapResolution ? size : tData.heightmapResolution - xBase;
int height = yBase + size <= tData.heightmapResolution ? size : tData.heightmapResolution - yBase;
// Get the height map within the brush range and normalize the height to [0, 1]
float[,] heights = tData.GetHeights(xBase, yBase, width, height);
float initHeight = tData.GetHeight(index.x, index.y) / tData.size.y;
float deltaHeight = -opacity / tData.size.y;
// Apply brush
ApplyBrush(heights, deltaHeight, initHeight, height, width);
tData.SetHeights(xBase, yBase, heights);
}
/// <summary>
/// Smooth according to the height of the four corners of the brush Terrain, This method does not change the Terrain Height .
/// </summary>
/// <param name="terrain">Terrain</param>
/// <param name="point"> World coordinate point </param>
/// <param name="opacity"> Smooth sensitivity , The value is between [0.05,1] Between </param>
/// <param name="size"> Brush size </param>
public static void Smooth(Terrain terrain, Vector3 point, float opacity, int size)
{
TerrainData tData = terrain.terrainData;
if (opacity > 1 || opacity <= 0)
{
opacity = Mathf.Clamp(opacity, 0.05f, 1);
Debug.LogError("Smooth Methods opacity The value of the parameter should be between [0.05,1] Between , Force it to :" + opacity);
}
// Take out... Within the brush range HeightMap Data array
Vector2Int index = GetHeightmapIntIndex(terrain, point);
int bound = size / 2;
int xBase = index.x - bound >= 0 ? index.x - bound : 0;
int yBase = index.y - bound >= 0 ? index.y - bound : 0;
int width = xBase + size <= tData.heightmapResolution ? size : tData.heightmapResolution - xBase;
int height = yBase + size <= tData.heightmapResolution ? size : tData.heightmapResolution - yBase;
float[,] heights = tData.GetHeights(xBase, yBase, width, height);
// Using a brush 4 The average height is calculated from the height of the angle
float avgHeight = (heights[0, 0] + heights[0, width - 1] + heights[height - 1, 0] + heights[height - 1, width - 1]) / 4;
Vector2 center = new Vector2((float)(height - 1) / 2, (float)(width - 1) / 2);
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
// The distance from the point to the center of the matrix
float toCenter = Vector2.Distance(center, new Vector2(i, j));
float diff = avgHeight - heights[i, j];
// The judgment point is 4 Positions on triangular blocks
// Using similar triangles, the distance between the point and the intersection of the extension line connecting the matrix center point and the point and the boundary is calculated
float d = 0;
if (i == height / 2 && j == width / 2) // Center point
{
d = 1;
toCenter = 0;
}
else if (i >= j && i <= size - j) // Left triangle
{
d = toCenter * j / ((float)width / 2 - j);
}
else if (i <= j && i <= size - j) // Upper triangle
{
d = toCenter * i / ((float)height / 2 - i);
}
else if (i <= j && i >= size - j) // Right triangle
{
d = toCenter * (size - j) / ((float)width / 2 - (size - j));
}
else if (i >= j && i >= size - j) // Lower triangle
{
d = toCenter * (size - i) / ((float)height / 2 - (size - i));
}
// The proportion of points raised or lowered when smoothing
float ratio = d / (d + toCenter);
heights[i, j] += diff * ratio * opacity;
}
}
tData.SetHeights(xBase, yBase, heights);
}
/// <summary>
/// Flatten Terrain And lift it to the specified height .
/// </summary>
/// <param name="terrain">Terrain</param>
/// <param name="height"> Height </param>
public static void Flatten(Terrain terrain, float height)
{
TerrainData tData = terrain.terrainData;
float scaledHeight = height / tData.size.y;
float[,] heights = new float[tData.heightmapResolution, tData.heightmapResolution];
for (int i = 0; i < tData.heightmapResolution; i++)
{
for (int j = 0; j < tData.heightmapResolution; j++)
{
heights[i, j] = scaledHeight;
}
}
tData.SetHeights(0, 0, heights);
}
/* Terrain Of HeightMap The coordinate origin is in the lower left corner * y * ↑ * 0 → x */
/// <summary> Returns a world coordinate point at Terrain Of HeightMap Indexes </summary>
private static Vector2Int GetHeightmapIntIndex(Terrain terrain, Vector3 point)
{
TerrainData tData = terrain.terrainData;
int x = (int)((point.x - terrain.GetPosition().x) / tData.size.x * tData.heightmapResolution);
int y = (int)((point.z - terrain.GetPosition().z) / tData.size.z * tData.heightmapResolution);
return new Vector2Int(x, y);
}
/// <summary> Returns a world coordinate point at Terrain Of HeightMap Indexes </summary>
private static Vector2 GetHeightmapFloatIndex(Terrain terrain, Vector3 point)
{
TerrainData tData = terrain.terrainData;
float x = (float)((point.x - terrain.GetPosition().x) / tData.size.x * tData.heightmapResolution);
float y = (float)((point.z - terrain.GetPosition().z) / tData.size.z * tData.heightmapResolution);
return new Vector2(x, y);
}
/// <summary> return GameObject stay Terrain Relative position on </summary>
private static Vector3 GetRelativePosition(Terrain terrain, GameObject go)
{
return go.transform.position - terrain.GetPosition();
}
/// <summary> Returns the specified point in the world coordinate system at Terrain A high degree of . </summary>
private static float GetPointHeight(Terrain terrain, Vector3 point, bool vertex = false)
{
// For a point on the horizontal plane ,vertex Parameters have no effect
if (vertex)
{
// GetHeight What you get is the height of the vertex closest to the point
Vector2Int index = GetHeightmapIntIndex(terrain, point);
return terrain.terrainData.GetHeight(index.x, index.y);
}
else
{
// SampleHeight What you get is the actual height of the point on the slope
return terrain.SampleHeight(point);
}
}
/// <summary>
/// return Terrain Of HeightMap, This is a height*width Two dimensional array of sizes , And the value is between [0.0f,1.0f] Between .
/// </summary>
/// <param name="terrain">Terrain</param>
/// <param name="xBase"> retrieval HeightMap At the time of the X Index start point </param>
/// <param name="yBase"> retrieval HeightMap At the time of the Y Index start point </param>
/// <param name="width"> stay X Retrieval length on axis </param>
/// <param name="height"> stay Y Retrieval length on axis </param>
/// <returns></returns>
public static float[,] GetHeightMap(Terrain terrain, int xBase = 0, int yBase = 0, int width = 0, int height = 0)
{
if (xBase + yBase + width + height == 0)
{
width = terrain.terrainData.heightmapResolution;
height = terrain.terrainData.heightmapResolution;
}
return terrain.terrainData.GetHeights(xBase, yBase, width, height);
}
/// <summary>
/// change heightmap The value in
/// </summary>
/// <param name="heights">HeightMap</param>
/// <param name="deltaHeight"> Height variation [-1,1]</param>
/// <param name="initHeight"> The initial height of the brush center point </param>
/// <param name="row">HeightMap Row number </param>
/// <param name="column">HeightMap Number of columns </param>
/// <param name="amass"> When the height of other points within the brush range is higher than the brush center point, whether to increase the height of other points at the same time </param>
private static void ApplyBrush(float[,] heights, float deltaHeight, float initHeight, int row, int column)
{
// Height limit
float limit = initHeight + deltaHeight;
for (int i = 0; i < row; i++)
{
for (int j = 0; j < column; j++)
{
if (deltaHeight > 0) // Raise the terrain
{
heights[i, j] = heights[i, j] >= limit ? heights[i, j] : heights[i, j] + deltaHeight;
}
else // Lower the terrain
{
heights[i, j] = heights[i, j] <= limit ? heights[i, j] : heights[i, j] + deltaHeight;
}
}
}
}
/// <summary>
/// Set up Terrain Of HeightMap.
/// </summary>
/// <param name="terrain">Terrain</param>
/// <param name="heights">HeightMap</param>
/// <param name="xBase">X The starting point </param>
/// <param name="yBase">Y The starting point </param>
private static void SetHeights(Terrain terrain, float[,] heights, int xBase = 0, int yBase = 0)
{
terrain.terrainData.SetHeights(xBase, yBase, heights);
}
#endregion
#region Terrain map brush
#endregion
}
边栏推荐
- 服务管理与通信,基础原理分析
- MySQL ---- 常用函数
- [FAQ] summary of common problems and solutions during the use of rest API interface of sports health service
- Analysis on rendering principle of mobile terminal
- 江波龙 FORESEE XP2000 PCIe 4.0 SSD 多重加密功能,锁定数据安全
- 老程序员说:别再直译这大千世界了,开发人员应该回归程序设计
- Electronic bidding procurement mall system: optimize traditional procurement business and speed up enterprise digital upgrading
- Finally, someone explained the difference among cookies, sessions and tokens. Detailed explanation, interview questions.
- MySQL Basics
- Elastic-Job的快速入门,三分钟带你体验分布式定时任务
猜你喜欢

Connexion MySQL errorcode 1129, State hy000, Host 'xxx' is Blocked because of many Connection Errors

Knowledge map / relationship visualization

聊聊服务器性能优化~(建议收藏)

mysql基础篇之mysql在已有表中添加自动增加的主键(或任意一个字段)

电子招标采购商城系统:优化传统采购业务,提速企业数字化升级

解决idea超过5个相同包的时候自动变成*的问题

Electronic bidding procurement mall system: optimize traditional procurement business and speed up enterprise digital upgrading

Canvas advanced functions (Part 1)

Service management and communication, basic principle analysis

自注意力(self-attention)和多头注意力(multi-head attention)
随机推荐
【电脑使用】如何设置没有自启项的软件开机启动
[generation confrontation network learning part I] classic Gan and its existing problems and related improvements
Pytorch deep learning -- neural network convolution layer conv2d
在阿里云国际上使用 OSS 和 CDN 部署静态网站
NetWorkX使用方法及 nx.draw()相关参数
Deploying static websites using OSS and CDN on Alibaba cloud international
RuntimeError: Attempting to deserialize object on CUDA device 1 but torch.cuda.device_count() is 1.
中国工科研究生200多篇英文论文中最常见的习惯(The Most Common Habits from more than 200 English Papers written by Gradua)
Microsoft Word tutorial, how to change page orientation and add borders to pages in word?
ResourceNotFoundException : Unable to find resource
vulnhub-The Planets: Earth
[Legendre] polynomial
js基础及常考面试题之 [] == ![]结果为true, []==[]结果为false 详解
为什么网页样式属性,有的需要加冒号“:”,有的不用?
Four methods to obtain the position index of the first n values of the maximum and minimum values in the list
详解三级缓存解决循环依赖
[computer use] how to set software startup without auto startup
移动端渲染原理浅析
Quick start to elastic job, three minutes to experience distributed scheduled tasks
Self attention and multi head attention