【Unity学習】リアルな水面効果開発
21519 ワード
この水面の特効は本当にやりにくいので、長い間調整してやっと書きました.
真実の水面を実現するための関連要件凹凸テクスチャを使用して水面に起伏感を与える、水面に微細なうねりを生じる効果 . FTTを用いて作成する水面の波動は、水面上のある点に対して、その現在の水波は、正舷波が重畳 を得ることができる.ハイライトを追加ハイライトを追加主に水面からの太陽光とライトの反射 である.反射を追加するのは、主に水面位置にカメラを使用し、このカメラが撮った画面を水平面に反転させることです. テクスチャ摂動は、反射された画像を水平面に貼ることができず、実際の水面のように凹凸のある効果を生じさせる必要があります.
インプリメンテーション
私は主に3つのファイルを使って水面効果を実現して、2つのcsファイル1つのshaderファイル
ファイル1:水面モデルにマウントされたWater_wave.csスクリプトの役割は水面の波紋を計算することであり,水波の発生点であり,それらが互いに重なり,波紋を生じることを定義した.
ファイル2:次は水面モデルにマウントされたMirrorです.csスクリプトその主な機能はレンダリングカメラを提供し、撮影したシーンをマップに保存することです.
実はこのコードは少し複雑で、私はまた基本的な考え方を最適化して、睡眠を鏡面の中心としてメインカメラに対してミラーカメラをミラーして、そしてこのカメラの裁断平面を計算することです.
ファイル3:水面のShaderスクリプトWaterShader
シェーダ(Shader)は、主に法線マップの使用、拡散マップの使用、ハイライトの追加、半透明(Translucency)、および法線のスクランブルテクスチャの使用です.
真実の水面を実現するための関連要件
インプリメンテーション
私は主に3つのファイルを使って水面効果を実現して、2つのcsファイル1つのshaderファイル
ファイル1:水面モデルにマウントされたWater_wave.csスクリプトの役割は水面の波紋を計算することであり,水波の発生点であり,それらが互いに重なり,波紋を生じることを定義した.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Water_wave : MonoBehaviour {
private Vector3[] vertices; //
private float mytime; //
public float waveFrequency1 = 0.3f; // 4
public float waveFrequency2 = 0.5f;
public float waveFrequency3 = 0.9f;
public float waveFrequency4 = 1.5f;
private Vector3 v_zero = Vector3.zero; //
public float Speed = 1; //
private int index1 = 760; //
private int index2 = 900; //
private int index3 = 120000; //
private Vector2 uv_offset = Vector2.zero; //
private Vector2 uv_direction = new Vector2(0.5f, 0.5f); //
// Use this for initialization
void Start () {
vertices = GetComponent().mesh.vertices; //
}
// Update is called once per frame
void Update () {
mytime += Time.deltaTime * Speed; //
for(int i=0; i < vertices.Length; i++)
{
vertices[i] = new Vector3(vertices[i].x, FindHeight(i), vertices[i].z); // y
}
GetComponent().mesh.vertices = vertices; //
uv_offset += (uv_direction * Time.deltaTime*0.1f); //
GetComponent().material.SetTextureOffset("_NormalTex",uv_offset); //
GetComponent().mesh.RecalculateNormals(); //
}
float FindHeight(int i)
{
float H = 0;
float distance1 = Vector2.Distance(new Vector2(vertices[i].x, vertices[i].z), v_zero); //
float distance2 = Vector2.Distance(new Vector2(vertices[i].x, vertices[i].z),
new Vector2(vertices[index1].x, vertices[index1].z)); //
float distance3 = Vector2.Distance(new Vector2(vertices[i].x, vertices[i].z),
new Vector2(vertices[index1].x, vertices[index1].z)); //
float distance4 = Vector2.Distance(new Vector2(vertices[i].x, vertices[i].z),
new Vector2(vertices[index1].x, vertices[index1].z)); //
// h= x xPI+
H = Mathf.Sin((distance1) * waveFrequency1 * Mathf.PI + mytime) / 30;
H += Mathf.Sin((distance2) * waveFrequency2 * Mathf.PI + mytime) / 25;
H += Mathf.Sin((distance3) * waveFrequency3 * Mathf.PI + mytime) / 35;
H += Mathf.Sin((distance4) * waveFrequency4 * Mathf.PI + mytime) / 40;
return i;
}
}
ファイル2:次は水面モデルにマウントされたMirrorです.csスクリプトその主な機能はレンダリングカメラを提供し、撮影したシーンをマップに保存することです.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Mirror : MonoBehaviour {
public RenderTexture refTex; //
public Matrix4x4 correction; //
public Matrix4x4 projM; //
Matrix4x4 world2ProjView; //
public Matrix4x4 cm; //
private Camera mirCam; //
private bool busy = false; //
void Start () {
if (mirCam) return; // ,
GameObject g = new GameObject("Mirror Camera"); //
mirCam = g.AddComponent(); //
mirCam.enabled = false;
refTex = new RenderTexture(800, 600, 16); //
refTex.hideFlags = HideFlags.DontSave; //
mirCam.targetTexture = refTex;
GetComponent().material.SetTexture("_MainTex",refTex); //
correction = Matrix4x4.identity; //
correction.SetColumn(3, new Vector4(0.5f, 0.5f, 0.5f, 1f)); //
correction.m00 = 0.5f; //
correction.m11 = 0.5f;
correction.m22 = 0.5f;
}
void Update () {
GetComponent().material.SetTexture("_MainTex", refTex); //
}
private void OnWillRenderObject() // ,
{
if (busy) return; // ?
busy = true; // ,
Camera cam = Camera.main; //
mirCam.CopyFrom(cam); //
mirCam.transform.parent = transform; //
Camera.main.transform.parent = transform; //
Vector3 mPos = mirCam.transform.localPosition; //
mPos.y *= -1f; //
mirCam.transform.localPosition = mPos; //
Vector3 rt = Camera.main.transform.localEulerAngles; //
Camera.main.transform.parent = null; //
mirCam.transform.localEulerAngles = new Vector3(-rt.x, rt.y, -rt.z); //
//
float d = Vector3.Dot(transform.up, Camera.main.transform.position - transform.position) + 0.05f;
mirCam.nearClipPlane = d; //
Vector3 pos = transform.position; //
Vector3 normal = transform.up; //
Vector4 clipPlane = CameraSpacePlane(mirCam, pos, normal, 1.0f); //
Matrix4x4 proj = cam.projectionMatrix; //
proj = cam.CalculateObliqueMatrix(clipPlane); //
mirCam.projectionMatrix = proj; //
mirCam.targetTexture = refTex; //
mirCam.Render(); //
Proj(); //
GetComponent().material.SetMatrix("_Projmat",cm); //
busy = false;
}
// : ,
void Proj()
{
world2ProjView = mirCam.transform.worldToLocalMatrix; //
projM = mirCam.projectionMatrix; //
projM.m32 = 1f; //
cm = correction * projM * world2ProjView; //
}
// , 。 : , , ,
private Vector4 CameraSpacePlane(Camera cam, Vector3 pos, Vector3 normal, float sideSign)
{
Vector3 offsetPos = pos + normal * -0.1f; //
Matrix4x4 m = cam.worldToCameraMatrix; //
Vector3 cpos = m.MultiplyPoint(offsetPos); //
Vector3 cnormal = m.MultiplyVector(normal).normalized * sideSign; //
return new Vector4(cnormal.x, cnormal.y, cnormal.z, -Vector3.Dot(cpos, cnormal)); //
}
}
実はこのコードは少し複雑で、私はまた基本的な考え方を最適化して、睡眠を鏡面の中心としてメインカメラに対してミラーカメラをミラーして、そしてこのカメラの裁断平面を計算することです.
ファイル3:水面のShaderスクリプトWaterShader
Shader "Custom/WaterShader" {
Properties{
_MainTint("Diffuse Tint", Color) = (1,1,1,0) //
_MainTex("Base (RGB)", 2D) = "white" {} //
_BackTint("Back Tint", Color) = (1,1,1,0) //
_BackTex("Background", 2D) = "white" {} //
_SpecColor("Specular Color", Color) = (1,1,1,1) //
_SpecPower("Specular Power", Range(0.5, 100)) = 3 //
_NormalTex("Normal Map", 2D) = "bump"{} //
_TransVal("Transparecy Value", Range(0, 1)) = 0.5 //
_PerturbationAmt("Perturbation Amt", Range(0, 1)) = 1 //
}
// 13
SubShader{
Tags { "Queue" = "Transparent-20" "RenderType" = "Opaque" } //
CGPROGRAM
#pragma surface surf CustomBlinnPhong vertex:vert alpha
#pragma target 3.0
#include "UnityCG.cginc"
float4 _MainTint;
sampler2D _MainTex;
float4 _BackTint;
sampler2D _BackTex;
//float4 _SpecColor;
float _SpecPower;
sampler2D _NormalTex;
float _TransVal;
float _PerturbationAmt;
float4x4 _ProjMat; //
// 30
struct Input {
float2 uv_MainTex; //
float2 uv_NormalTex; //
float4 pos; //
float4 texc; //
INTERNAL_DATA
};
inline fixed4 LightingCustomBlinnPhong(SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten){
float3 halfVector = normalize(lightDir+viewDir); // h
float diff = max(0,dot(s.Normal,lightDir)); // *
float nh = max(0,dot(s.Normal,halfVector)); // * h
float spec = pow(nh,_SpecPower)*_SpecColor; //
float4 c; //
c.rgb=(s.Albedo*_LightColor0.rgb*diff)+(_LightColor0.rgb*_SpecColor.rgb*spec)*(atten*2); //
c.a = s.Alpha; //
return c; //
}
// 50
void vert (inout appdata_full v, out Input o) {
UNITY_INITIALIZE_OUTPUT(Input, o); // o
o.pos=v.vertex; // pos
}
void surf (Input IN, inout SurfaceOutput o){
float4x4 proj=mul(_ProjMat, _Object2World); //
IN.texc=mul(proj, IN.pos); // proj
float4 c_Back = tex2D(_BackTex, IN.uv_MainTex); //
float3 normalMap = UnpackNormal(tex2D(_NormalTex, IN.uv_NormalTex)); //
half2 offset=IN.texc.rg/IN.texc.w; //
offset.x = offset.x+_PerturbationAmt*offset.x*normalMap.x; // x
offset.y = offset.y+_PerturbationAmt*offset.y*normalMap.y; // y
float4 c_Main = tex2D(_MainTex, offset) * _MainTint; //
float3 finalColor = lerp(c_Back, c_Main, 0.7).rgb*_BackTint; //
o.Normal = normalize(normalMap.rgb+o.Normal.rgb); //
o.Specular = _SpecPower; //
o.Gloss = 1.0; //
o.Albedo = finalColor; //
o.Alpha = (c_Main.a*0.5+0.5)*_TransVal; //
}
ENDCG
}
FallBack "Diffuse"
}
シェーダ(Shader)は、主に法線マップの使用、拡散マップの使用、ハイライトの追加、半透明(Translucency)、および法線のスクランブルテクスチャの使用です.