gpt4 book ai didi

c# - 在 Unity 中使用 Shells 技术实现 Fur

转载 作者:太空狗 更新时间:2023-10-29 17:42:54 25 4
gpt4 key购买 nike

我正在尝试使用 Shells technique 在 Unity 中实现毛发. Fins 技术被故意排除在外,因为我希望它在低端手机(主要是 Android 设备)上运行,并且需要 OpenGL ES 3.0 及更高版本,而 Shells 技术 只需要 em>OpenGL ES 2.0

有一个基于 XNA 的 Shell 技术的示例,我尝试将其移植到 Unity 中,但没有成功。 Here是 XNA 项目的文章。

XNA 着色器:

float4x4 World;
float4x4 View;
float4x4 Projection;

float CurrentLayer; //value between 0 and 1
float MaxHairLength; //maximum hair length

texture FurTexture;
sampler FurSampler = sampler_state
{
Texture = (FurTexture);
MinFilter = Point;
MagFilter = Point;
MipFilter = Point;
AddressU = Wrap;
AddressV = Wrap;
};


struct VertexShaderInput
{
float3 Position : POSITION0;
float3 Normal : NORMAL0;
float2 TexCoord : TEXCOORD0;
};

struct VertexShaderOutput
{
float4 Position : POSITION0;
float2 TexCoord : TEXCOORD0;
};

VertexShaderOutput FurVertexShader(VertexShaderInput input)
{
VertexShaderOutput output;
float3 pos;
pos = input.Position + input.Normal * MaxHairLength * CurrentLayer;

float4 worldPosition = mul(float4(pos,1), World);
float4 viewPosition = mul(worldPosition, View);
output.Position = mul(viewPosition, Projection);

output.TexCoord = input.TexCoord;
return output;
}

float4 FurPixelShader(VertexShaderOutput input) : COLOR0
{
return tex2D(FurSampler, input.TexCoord);
}

technique Fur
{
pass Pass1
{
AlphaBlendEnable = true;
SrcBlend = SRCALPHA;
DestBlend = INVSRCALPHA;
CullMode = None;

VertexShader = compile vs_2_0 FurVertexShader();
PixelShader = compile ps_2_0 FurPixelShader();
}
}

控制着色器的 XNA C# 脚本:

/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;

public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}

//simple camera for use in the game
Camera camera;
//texture containing fur data
Texture2D furTexture;
//effect for fur shaders
Effect furEffect;
//number of layers of fur
int nrOfLayers = 60;
//total length of the hair
float maxHairLength = 2.0f;
//density of hair
float density = 0.2f;
Texture2D furColorTexture;

//movement vectors
Vector3 gravity = new Vector3(0, -1.0f, 0);
Vector3 forceDirection = Vector3.Zero;
//final displacement for hair
Vector3 displacement;


/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
// TODO: Add your initialization logic here
camera = new Camera(this);
Components.Add(camera);
base.Initialize();
}

/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
//generate the geometry
GenerateGeometry();
//load the effect
furEffect = Content.Load<Effect>("FurEffect");
//create the texture
furTexture = new Texture2D(GraphicsDevice,
256, 256, 1,
TextureUsage.None,
SurfaceFormat.Color);
//fill the texture
FillFurTexture(furTexture, density);
furColorTexture = Content.Load<Texture2D>("bigtiger");
}

/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// all content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}

/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();

// TODO: Add your update logic here

base.Update(gameTime);
}

/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
forceDirection.X = (float)Math.Sin(gameTime.TotalGameTime.TotalSeconds) * 0.5f;
displacement = gravity + forceDirection;
furEffect.Parameters["Displacement"].SetValue(displacement);

graphics.GraphicsDevice.Clear(Color.CornflowerBlue);

furEffect.Parameters["World"].SetValue(Matrix.CreateTranslation(0, -10, 0));
furEffect.Parameters["View"].SetValue(camera.View);
furEffect.Parameters["Projection"].SetValue(camera.Projection);
furEffect.Parameters["MaxHairLength"].SetValue(maxHairLength);
furEffect.Parameters["FurTexture"].SetValue(furTexture);
furEffect.Parameters["Texture"].SetValue(furColorTexture);

furEffect.Begin();
for (int i = 0; i < nrOfLayers; i++)
{
furEffect.Parameters["CurrentLayer"].SetValue((float)i / nrOfLayers);
furEffect.CommitChanges();
furEffect.CurrentTechnique.Passes[0].Begin();
DrawGeometry();
furEffect.CurrentTechnique.Passes[0].End();
}
furEffect.End();

base.Draw(gameTime);
}

/// <summary>
/// This functions prepares a texture to be used for fur rendering
/// </summary>
/// <param name="furTexture">This will contain the final texture</param>
/// <param name="density">Hair density in [0..1] range </param>
private void FillFurTexture(Texture2D furTexture, float density)
{
//read the width and height of the texture
int width = furTexture.Width;
int height = furTexture.Height;
int totalPixels = width * height;

//an array to hold our pixels
Color[] colors;
colors = new Color[totalPixels];

//random number generator
Random rand = new Random();

//initialize all pixels to transparent black
for (int i = 0; i < totalPixels; i++)
colors[i] = Color.TransparentBlack;

//compute the number of opaque pixels = nr of hair strands
int nrStrands = (int)(density * totalPixels);

//compute the number of strands that stop at each layer
int strandsPerLayer = nrStrands / nrOfLayers;

//fill texture with opaque pixels
for (int i = 0; i < nrStrands; i++)
{
int x, y;
//random position on the texture
x = rand.Next(height);
y = rand.Next(width);

//compute max layer
int max_layer = i / strandsPerLayer;
//normalize into [0..1] range
float max_layer_n = (float)max_layer / (float)nrOfLayers;

//put color (which has an alpha value of 255, i.e. opaque)
//max_layer_n needs to be multiplied by 255 to achieve a color in [0..255] range
colors[x * width + y] = new Color((byte)(max_layer_n * 255), 0, 0, 255);
}

//set the pixels on the texture.
furTexture.SetData<Color>(colors);
}


VertexPositionNormalTexture[] vertices;

private void GenerateGeometry()
{
vertices = new VertexPositionNormalTexture[6];
vertices[0] = new VertexPositionNormalTexture(
new Vector3(-10, 0, 0),
-Vector3.UnitZ,
new Vector2(0, 0));
vertices[1] = new VertexPositionNormalTexture(
new Vector3(10, 20, 0),
-Vector3.UnitZ,
new Vector2(1, 1));
vertices[2] = new VertexPositionNormalTexture(
new Vector3(-10, 20, 0),
-Vector3.UnitZ,
new Vector2(0, 1));

vertices[3] = vertices[0];
vertices[4] = new VertexPositionNormalTexture(
new Vector3(10, 0, 0),
-Vector3.UnitZ,
new Vector2(1, 0));
vertices[5] = vertices[1];
}

private void DrawGeometry()
{
using (VertexDeclaration vdecl = new VertexDeclaration(
GraphicsDevice,
VertexPositionNormalTexture.VertexElements))
{
GraphicsDevice.VertexDeclaration = vdecl;
GraphicsDevice.DrawUserPrimitives<VertexPositionNormalTexture>(PrimitiveType.TriangleList, vertices, 0, 2);
}
}

}



我仔细地将着色器和控制脚本逐行移植到 Unity。

移植的 Unity 着色器:

Shader "Programmer/Fur Shader"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
//_TintColor("Tint Color", Color) = (1,1,1,1)
}
SubShader
{
Tags{ "Queue" = "Transparent" "RenderType" = "Transparent" }
LOD 100
Blend SrcAlpha One
Blend DstAlpha OneMinusSrcAlpha
ZWrite Off
Cull Off

Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
//#pragma multi_compile_fog

#include "UnityCG.cginc"

//In
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};

//Out
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};

struct VertexShaderInput
{
float3 Position : POSITION0;
float3 Normal : NORMAL0;
float2 TexCoord : TEXCOORD0;
};

struct VertexShaderOutput
{
float4 Position : POSITION0;
float2 TexCoord : TEXCOORD0;
};

sampler2D _MainTex;
float4 _MainTex_ST;

//Test variable/delete after
float4 _TintColor;

//The variables
float4x4 World;
float4x4 View;
float4x4 Projection;

float CurrentLayer; //value between 0 and 1
float MaxHairLength; //maximum hair length

VertexShaderOutput vert(VertexShaderInput input)
{
VertexShaderOutput output;
float3 pos;
pos = input.Position + input.Normal * MaxHairLength * CurrentLayer;

float4 worldPosition = mul(float4(pos, 1), World);
float4 viewPosition = mul(worldPosition, View);
output.Position = mul(viewPosition, Projection);

output.TexCoord = input.TexCoord;
return output;
}

float4 frag(VertexShaderOutput i) : COLOR0
{
return tex2D(_MainTex, i.TexCoord);
}
ENDCG
}
}
}

控制着色器的移植 Unity C# 脚本:

public class Game1 : MonoBehaviour
{
public Material material;


public Vector3 pos = new Vector3(0f, 0.98f, -9.54f);


//simple camera for use in the game
private new Camera camera;
//texture containing fur data
public Texture2D furTexture;
//effect for fur shaders
//Effect furEffect;
//number of layers of fur
public int nrOfLayers = 40;
//total length of the hair
public float maxHairLength = 2.0f;
//density of hair
public float density = 0.2f;

//[Space(20)]
//public Vector3 dirWorldVal = new Vector3(0, -10, 0);

void Start()
{
Initialize();
GenerateGeometry();
}

public void Update()
{
Draw();
}


void Initialize()
{

//Initialize the camera
camera = Camera.main;

//create the texture
furTexture = new Texture2D(256, 256, TextureFormat.ARGB32, false);
furTexture.wrapModeU = TextureWrapMode.Repeat;
furTexture.wrapModeV = TextureWrapMode.Repeat;
furTexture.filterMode = FilterMode.Point;

//fill the texture
FillFurTexture(furTexture, density);

/*XNA's SurfaceFormat.Color is ARGB.
//https://gamedev.stackexchange.com/a/6442/98839*/


if (material.mainTexture != null)
{
material.mainTexture.wrapModeU = TextureWrapMode.Repeat;
material.mainTexture.wrapModeV = TextureWrapMode.Repeat;
material.mainTexture.filterMode = FilterMode.Point;
}
}

bool firstDraw = true;

protected void Draw()
{
camera.backgroundColor = CornflowerBlue();

Matrix4x4 worldValue = Matrix4x4.Translate(pos);
Matrix4x4 viewValue = camera.projectionMatrix;
// viewValue = camera.worldToCameraMatrix;
Matrix4x4 projectionValue = camera.projectionMatrix;

material.SetMatrix("World", worldValue);
material.SetMatrix("View", viewValue);
material.SetMatrix("Projection", projectionValue); //Causes object to disappear

material.SetFloat("MaxHairLength", maxHairLength);

if (firstDraw)
material.SetTexture("_MainTex", furTexture);

//furEffect.Begin();
for (int i = 0; i < nrOfLayers; i++)
{
material.SetFloat("CurrentLayer", (float)i / nrOfLayers);
DrawGeometry();
}

if (firstDraw)
{
material.mainTexture.wrapModeU = TextureWrapMode.Repeat;
material.mainTexture.wrapModeV = TextureWrapMode.Repeat;
material.mainTexture.filterMode = FilterMode.Point;
}

if (firstDraw)
firstDraw = false;
}

void DrawGeometry()
{
Quaternion rotation = Quaternion.Euler(0, 180, 0);
Graphics.DrawMesh(verticesMesh, pos, rotation, material, 0, camera);
}

private VertexPositionNormalTexture[] verticesPText;
public Mesh verticesMesh;

private void GenerateGeometry()
{
verticesPText = new VertexPositionNormalTexture[6];
verticesPText[0] = new VertexPositionNormalTexture(new Vector3(-10, 0, 0),
-UnitZ(),
new Vector2(0, 0));
verticesPText[1] = new VertexPositionNormalTexture(new Vector3(10, 20, 0),
-UnitZ(),
new Vector2(1, 1));
verticesPText[2] = new VertexPositionNormalTexture(new Vector3(-10, 20, 0),
-UnitZ(),
new Vector2(0, 1));

verticesPText[3] = verticesPText[0];
verticesPText[4] = new VertexPositionNormalTexture(new Vector3(10, 0, 0),
-UnitZ(),
new Vector2(1, 0));
verticesPText[5] = verticesPText[1];

verticesMesh = VertexPositionNormalTextureToUnityMesh(verticesPText);
}

Mesh VertexPositionNormalTextureToUnityMesh(VertexPositionNormalTexture[] vpnt)
{
Vector3[] vertices = new Vector3[vpnt.Length];
Vector3[] normals = new Vector3[vpnt.Length];
Vector2[] uvs = new Vector2[vpnt.Length];

int[] triangles = new int[vpnt.Length];

//Copy variables to create a mesh
for (int i = 0; i < vpnt.Length; i++)
{
vertices[i] = vpnt[i].Position;
normals[i] = vpnt[i].Normal;
uvs[i] = vpnt[i].TextureCoordinate;

triangles[i] = i;
}

Mesh mesh = new Mesh();
mesh.vertices = vertices;
mesh.normals = normals;
mesh.uv = uvs;

mesh.triangles = triangles;
return mesh;
}

private void FillFurTexture(Texture2D furTexture, float density)
{
//read the width and height of the texture
int width = furTexture.width;
int height = furTexture.height;
int totalPixels = width * height;

//an array to hold our pixels
Color32[] colors = new Color32[totalPixels];

//random number generator
System.Random rand = new System.Random();

//initialize all pixels to transparent black
for (int i = 0; i < totalPixels; i++)
colors[i] = TransparentBlack();

//compute the number of opaque pixels = nr of hair strands
int nrStrands = (int)(density * totalPixels);

//fill texture with opaque pixels
for (int i = 0; i < nrStrands; i++)
{
int x, y;
//random position on the texture
x = rand.Next(height);
y = rand.Next(width);
//put color (which has an alpha value of 255, i.e. opaque)
colors[x * width + y] = Gold();
}

//set the pixels on the texture.
furTexture.SetPixels32(colors);
// actually apply all SetPixels, don't recalculate mip levels
furTexture.Apply();
}

Color32 TransparentBlack()
{
//http://www.monogame.net/documentation/?page=P_Microsoft_Xna_Framework_Color_TransparentBlack
Color32 color = new Color32(0, 0, 0, 0);
return color;
}

Color32 Gold()
{
//http://www.monogame.net/documentation/?page=P_Microsoft_Xna_Framework_Color_Gold
Color32 color = new Color32(255, 215, 0, 255);
return color;
}

Color32 CornflowerBlue()
{
//http://www.monogame.net/documentation/?page=P_Microsoft_Xna_Framework_Color_CornflowerBlue
Color32 color = new Color32(100, 149, 237, 255);
return color;
}

public static Vector3 UnitZ()
{
return new Vector3(0f, 0f, 1f);
}
}

Unity 移植的 VertexPositionNormalTexture 结构

public struct VertexPositionNormalTexture
{
public Vector3 Position;
public Vector3 Normal;
public Vector2 TextureCoordinate;
//public static readonly VertexDeclaration VertexDeclaration;
public VertexPositionNormalTexture(Vector3 position, Vector3 normal, Vector2 textureCoordinate)
{
this.Position = position;
this.Normal = normal;
this.TextureCoordinate = textureCoordinate;
}

public override int GetHashCode()
{
// TODO: FIc gethashcode
return 0;
}

public override string ToString()
{
return string.Format("{{Position:{0} Normal:{1} TextureCoordinate:{2}}}", new object[] { this.Position, this.Normal, this.TextureCoordinate });
}

public static bool operator ==(VertexPositionNormalTexture left, VertexPositionNormalTexture right)
{
return (((left.Position == right.Position) && (left.Normal == right.Normal)) && (left.TextureCoordinate == right.TextureCoordinate));
}

public static bool operator !=(VertexPositionNormalTexture left, VertexPositionNormalTexture right)
{
return !(left == right);
}

public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (obj.GetType() != base.GetType())
{
return false;
}
return (this == ((VertexPositionNormalTexture)obj));
}
}

移植的 Unity 工作不正常。没有壳,输出图像是平面的。

这是 XNA 中的预期结果(工作正常):

enter image description here

但这是我在 Unity 中看到的(没有外壳):

enter image description here

最终图像应该如下图所示,但我无法继续进行移植工作,因为基本实现不能在 Unity 中正常工作。

enter image description here

我的脚本公共(public)变量设置:

enter image description here

为什么移植后的 Unity 结果是平的?我错过了什么吗?

编辑:

Leo提到可能的backface问题,因为Unity使用左手坐标系,而XNA使用右手坐标系。

我翻转了 UnitZ() 值并尝试反转网格顶点,但屏幕上没有任何内容。 这不太可能是问题所在。

最佳答案

Unity 正在对 Material 进行批量优化。您可以在帧调试器中看到这一点。每个 DrawGeometry 调用都使用相同的 CurrentLayer 值。每次调用 DrawMesh 都需要使用属性 block 。设置新 Material 会导致一些闪烁。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

namespace foo {
public class FurBehavior : MonoBehaviour
{
public Material material;


public Vector3 pos = new Vector3(0f, 0.98f, -9.54f);


//simple camera for use in the game
private new Camera camera;
//texture containing fur data
public Texture2D furTexture;
//effect for fur shaders
//Effect furEffect;
//number of layers of fur
public int nrOfLayers = 40;
//total length of the hair
public float maxHairLength = 2.0f;
//density of hair
public float density = 0.2f;

//[Space(20)]
//public Vector3 dirWorldVal = new Vector3(0, -10, 0);

void Start()
{
this.transform.position = new Vector3(0f, 0.98f, -9.54f);
this.transform.rotation = Quaternion.Euler(0, 180, 0);
Initialize();
GenerateGeometry();
}

public void Update()
{
Draw();

}


void Initialize()
{

//Initialize the camera
camera = Camera.main;

//create the texture
furTexture = new Texture2D(256, 256, TextureFormat.ARGB32, false);
furTexture.wrapModeU = TextureWrapMode.Repeat;
furTexture.wrapModeV = TextureWrapMode.Repeat;
//furTexture.filterMode = FilterMode.Point;

//fill the texture
FillFurTexture(furTexture, density);

/*XNA's SurfaceFormat.Color is ARGB.
//https://gamedev.stackexchange.com/a/6442/98839*/


if (material.mainTexture != null)
{
material.mainTexture.wrapModeU = TextureWrapMode.Repeat;
material.mainTexture.wrapModeV = TextureWrapMode.Repeat;
// material.mainTexture.filterMode = FilterMode.Point;
}
}

bool firstDraw = true;

protected void Draw()
{
var pos = this.transform.position;

camera.backgroundColor = CornflowerBlue();

Matrix4x4 worldValue = Matrix4x4.Translate(pos);
Matrix4x4 viewValue = camera.projectionMatrix;
// viewValue = camera.worldToCameraMatrix;
Matrix4x4 projectionValue = camera.projectionMatrix;

material.SetMatrix("World", worldValue);
material.SetMatrix("View", viewValue);
material.SetMatrix("Projection", projectionValue); //Causes object to disappear

material.SetFloat("MaxHairLength", maxHairLength);

//if (firstDraw)
material.SetTexture("_MainTex", furTexture);

//furEffect.Begin();
for (int i = 0; i < nrOfLayers; i++)
{
var propertyBlock = new MaterialPropertyBlock();

var layer = (float)i / (float)nrOfLayers;
propertyBlock.SetFloat("CurrentLayer", layer);
propertyBlock.SetFloat("MaxHairLength", maxHairLength);
propertyBlock.SetColor("_TintColor", new Color(layer, layer, layer, layer));
DrawGeometry(propertyBlock);
}

if (firstDraw)
{
material.mainTexture.wrapModeU = TextureWrapMode.Repeat;
material.mainTexture.wrapModeV = TextureWrapMode.Repeat;
material.mainTexture.filterMode = FilterMode.Point;
}

if (firstDraw)
firstDraw = false;
}

void DrawGeometry(MaterialPropertyBlock props)
{
var rot = Quaternion.Euler(0, 180, 0);
Graphics.DrawMesh(verticesMesh, pos, rot, material, 0, camera, 0, props);
}

private VertexPositionNormalTexture[] verticesPText;
public Mesh verticesMesh;

private void GenerateGeometry()
{
var UnitZ = new Vector3(0, 0, 1);
var verticesPText = new VertexPositionNormalTexture[6];
verticesPText[5] = new VertexPositionNormalTexture(new Vector3(-10, 0, 0),
-UnitZ,
new Vector2(0, 0));
verticesPText[4] = new VertexPositionNormalTexture(new Vector3(10, 20, 0),
-UnitZ,
new Vector2(1, 1));
verticesPText[3] = new VertexPositionNormalTexture(new Vector3(-10, 20, 0),
-UnitZ,
new Vector2(0, 1));

verticesPText[2] = verticesPText[5];
verticesPText[1] = new VertexPositionNormalTexture(new Vector3(10, 0, 0),
-UnitZ,
new Vector2(1, 0));
verticesPText[0] = verticesPText[4];

}

Mesh VertexPositionNormalTextureToUnityMesh(VertexPositionNormalTexture[] vpnt)
{
Vector3[] vertices = new Vector3[vpnt.Length];
Vector3[] normals = new Vector3[vpnt.Length];
Vector2[] uvs = new Vector2[vpnt.Length];

int[] triangles = new int[vpnt.Length];

//Copy variables to create a mesh
for (int i = 0; i < vpnt.Length; i++)
{
vertices[i] = vpnt[i].Position;
normals[i] = vpnt[i].Normal;
uvs[i] = vpnt[i].TextureCoordinate;

triangles[i] = i;
}

Mesh mesh = new Mesh();
mesh.vertices = vertices;
mesh.normals = normals;
mesh.uv = uvs;

mesh.MarkDynamic();


mesh.triangles = triangles;
mesh.UploadMeshData(false);
return mesh;
}

private void FillFurTexture(Texture2D furTexture, float density)
{
//read the width and height of the texture
int width = furTexture.width;
int height = furTexture.height;
int totalPixels = width * height;

//an array to hold our pixels
Color32[] colors = new Color32[totalPixels];

//random number generator
System.Random rand = new System.Random();

//initialize all pixels to transparent black
for (int i = 0; i < totalPixels; i++)
colors[i] = TransparentBlack();

//compute the number of opaque pixels = nr of hair strands
int nrStrands = (int)(density * totalPixels);

//fill texture with opaque pixels
for (int i = 0; i < nrStrands; i++)
{
int x, y;
//random position on the texture
x = rand.Next(height);
y = rand.Next(width);
//put color (which has an alpha value of 255, i.e. opaque)
// colors[x * width + y] = new Color32((byte)255, (byte)x, (byte)y, (byte)255);
colors[x * width + y] = Gold();
}

//set the pixels on the texture.
furTexture.SetPixels32(colors);
// actually apply all SetPixels, don't recalculate mip levels
furTexture.Apply();
}

Color32 TransparentBlack()
{
//http://www.monogame.net/documentation/?page=P_Microsoft_Xna_Framework_Color_TransparentBlack
Color32 color = new Color32(0, 0, 0, 0);
return color;
}

Color32 Gold()
{
//http://www.monogame.net/documentation/?page=P_Microsoft_Xna_Framework_Color_Gold
Color32 color = new Color32(255, 215, 0, 255);
return color;
}

Color32 CornflowerBlue()
{
//http://www.monogame.net/documentation/?page=P_Microsoft_Xna_Framework_Color_CornflowerBlue
Color32 color = new Color32(100, 149, 237, 255);
return color;
}

public static Vector3 UnitZ()
{
return new Vector3(0f, 0f, 1f);
}
}
}

我还修改了着色器以可视化外壳。

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 
'UnityObjectToClipPos(*)'

Shader "Programmer/Fur Shader"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
_TintColor("Tint Color", Color) = (1,1,1,1)
}
SubShader
{
Tags{ "Queue" = "Transparent" "RenderType" = "Transparent" }
LOD 100
//Blend SrcAlpha One
//Blend DstAlpha OneMinusSrcAlpha
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off
Cull Off

Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
//#pragma multi_compile_fog

#include "UnityCG.cginc"

//In
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};

//Out
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};

struct VertexShaderInput
{
float3 Position : POSITION0;
float3 Normal : NORMAL0;
float2 TexCoord : TEXCOORD0;
};

struct VertexShaderOutput
{
float4 Position : POSITION0;
float2 TexCoord : TEXCOORD0;
float4 Tint: COLOR1;
};

sampler2D _MainTex;
float4 _MainTex_ST;

//Test variable/delete after
float4 _TintColor;

//The variables
float4x4 World;
float4x4 View;
float4x4 Projection;

float CurrentLayer; //value between 0 and 1
float MaxHairLength; //maximum hair length

VertexShaderOutput vert(VertexShaderInput input)
{
VertexShaderOutput output;
float3 pos;
pos = input.Position + input.Normal * MaxHairLength * CurrentLayer;

//float4 worldPosition = mul(float4(pos, 1), World);
//float4 viewPosition = mul(worldPosition, View);
output.Position = UnityObjectToClipPos(pos);

output.TexCoord = input.TexCoord;
output.Tint = float4(CurrentLayer, CurrentLayer, 0, 1);
return output;
}

float4 frag(VertexShaderOutput i) : COLOR0
{
float4 t = tex2D(_MainTex, i.TexCoord) * i.Tint;
return t;//float4(t, i.x, i.y, 1);
}
ENDCG
}
}

这是在弄乱参数并稍微移动相机后的样子。

Looking at it from around {0, 0, -10}

关于c# - 在 Unity 中使用 Shells 技术实现 Fur,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52721907/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com