我正在尝试使用Shells技术在Unity中实现毛发.Fins技术是故意遗漏的,因为我希望它在低端手机(主要是Android设备)上运行,并且需要OpenGL ES 3.0及更高版本,而Shells技术只需要OpenGL ES 2.0.
有一个基于XNA的Shell技术的例子,我试图将它移植到Unity但它无法工作.这是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#脚本:
////// This is the main type for your game /// 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; ////// 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. /// protected override void Initialize() { // TODO: Add your initialization logic here camera = new Camera(this); Components.Add(camera); base.Initialize(); } ////// LoadContent will be called once per game and is the place to load /// all of your content. /// 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("FurEffect"); //create the texture furTexture = new Texture2D(GraphicsDevice, 256, 256, 1, TextureUsage.None, SurfaceFormat.Color); //fill the texture FillFurTexture(furTexture, density); furColorTexture = Content.Load ("bigtiger"); } /// /// UnloadContent will be called once per game and is the place to unload /// all content. /// protected override void UnloadContent() { // TODO: Unload any non ContentManager content here } ////// Allows the game to run logic such as updating the world, /// checking for collisions, gathering input, and playing audio. /// /// Provides a snapshot of timing values. 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); } ////// This is called when the game should draw itself. /// /// Provides a snapshot of timing values. 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/// This functions prepares a texture to be used for fur rendering /// /// This will contain the final texture /// Hair density in [0..1] range 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 (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 (PrimitiveType.TriangleList, vertices, 0, 2); } } }
我小心地将着色器和控制脚本逐行移植到Unity.
Ported 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; iUnity的移植
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中的预期结果(工作正常):
但这是我在Unity中看到的(没有炮弹):
最终的图像应该看起来像下面的图像,但我不能继续进行移植工作,因为基本实现在Unity中无法正常工作.
我的脚本公共变量设置:
为什么移植的Unity结果是平的?我错过了什么吗?
编辑:
Leo提到了可能的背面问题,因为Unity使用左手坐标系,而XNA使用右手坐标系.
我翻转了
UnitZ()
值并尝试反转网格顶点,但屏幕上没有任何内容.这不太可能是问题.
1> Leo Bartkus..:Unity正在对材料进行批量优化.您可以在帧调试器中看到这一点.每个DrawGeometry调用对CurrentLayer使用相同的值.每次调用DrawMesh时都需要使用propertyblock.设置新材料会导致一些闪烁.
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我还修改了着色器以显示shell.
// 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 } }}
这是在弄乱参数并稍微移动相机后的样子.