Unity3d 实时折射与反射

这里只贴出了实时折射与反射的脚本与shader,

关于NGUI部分则请大家自行下载。

这个版本主要缺点是折射部分平面的Layer必须是water层,如果有哪位高手能把这一块去掉,请记得把代码回复到这篇文章下,谢谢!

Water.cs

using UnityEngine;
using System.Collections;
using System;
using System.Collections.Generic;
/// <summary>
/// 水面
/// </summary>
[AddComponentMenu("GameCore/Effect/Water/Water (Base)")]
[ExecuteInEditMode]
public class Water : MonoBehaviour
{
    public enum FlageWaterRefType
    {
        Both = 0,
        Reflection = 1,
        Refraction = 2
    }
    public bool DisablePixelLights = false;
    public LayerMask Layers = -1;
    public int TexSize = 512;
    public FlageWaterRefType RefType = FlageWaterRefType.Both;
    public float ReflectClipPlaneOffset = 0;
    public float RefractionAngle = 0;

    private static Camera _reflectionCamera;
    private static Camera _refractionCamera;

    private int _OldTexSize = 0;
    private RenderTexture _reflectionRenderTex;
    private RenderTexture _refractionRenderTex;

    private bool _insideRendering = false;
    private float _refType = (float)FlageWaterRefType.Both;

    void OnWillRenderObject()
    {

        if (!enabled || !renderer || !renderer.sharedMaterial || !renderer.enabled)
            return;
        Camera cam = Camera.current;
        if (!cam)
            return;
        Material[] materials = renderer.sharedMaterials;
        if (_insideRendering)
            return;
        _insideRendering = true;
        int oldPixelLightCount = QualitySettings.pixelLightCount;
        if (DisablePixelLights)
            QualitySettings.pixelLightCount = 0;
        if (RefType == FlageWaterRefType.Both || RefType == FlageWaterRefType.Reflection)
        {
            DrawReflectionRenderTexture(cam);
            foreach (Material mat in materials)
            {
                if (mat.HasProperty("_ReflectionTex"))
                    mat.SetTexture("_ReflectionTex", _reflectionRenderTex);
            }
        }

        if (RefType == FlageWaterRefType.Both || RefType == FlageWaterRefType.Refraction)
        {
            this.gameObject.layer = 4;
            DrawRefractionRenderTexture(cam);
            foreach (Material mat in materials)
            {
                if (mat.HasProperty("_RefractionTex"))
                    mat.SetTexture("_RefractionTex", _refractionRenderTex);
            }
        }
        _refType = (float)RefType;
        Matrix4x4 projmtx = CoreTool.UV_Tex2DProj2Tex2D(transform, cam);
        foreach (Material mat in materials)
        {
            mat.SetMatrix("_ProjMatrix", projmtx);
            mat.SetFloat("_RefType", _refType);
        }
        if (DisablePixelLights)
            QualitySettings.pixelLightCount = oldPixelLightCount;
        _insideRendering = false;
    }

    /// <summary>
    /// 绘制反射RenderTexture
    /// </summary>
    private void DrawReflectionRenderTexture(Camera cam)
    {
        Vector3 pos = transform.position;
        Vector3 normal = transform.up;

        CreateObjects(cam,ref _reflectionRenderTex, ref _reflectionCamera);

        CoreTool.CloneCameraModes(cam, _reflectionCamera);

        float d = -Vector3.Dot(normal, pos) - ReflectClipPlaneOffset;
        Vector4 reflectionPlane = new Vector4(normal.x, normal.y, normal.z, d);

        Matrix4x4 reflection = CoreTool.CalculateReflectionMatrix(Matrix4x4.zero, reflectionPlane);

        Vector3 oldpos = cam.transform.position;
        Vector3 newpos = reflection.MultiplyPoint(oldpos);
        _reflectionCamera.worldToCameraMatrix = cam.worldToCameraMatrix * reflection;

        // Setup oblique projection matrix so that near plane is our reflection
        // plane. This way we clip everything below/above it for free.
        Vector4 clipPlane = CoreTool.CameraSpacePlane(_reflectionCamera, pos, normal, 1.0f, ReflectClipPlaneOffset);

        Matrix4x4 projection = cam.projectionMatrix;

        projection = CoreTool.CalculateObliqueMatrix(projection, clipPlane, -1);

        _reflectionCamera.projectionMatrix = projection;

        _reflectionCamera.cullingMask = ~(1 << 4) & Layers.value; // never render water layer
        _reflectionCamera.targetTexture = _reflectionRenderTex;

        GL.SetRevertBackfacing(true);
        _reflectionCamera.transform.position = newpos;
        Vector3 euler = cam.transform.eulerAngles;
        _reflectionCamera.transform.eulerAngles = new Vector3(0, euler.y, euler.z);
        _reflectionCamera.Render();
        _reflectionCamera.transform.position = oldpos;
        GL.SetRevertBackfacing(false);
    }

    /// <summary>
    /// 绘制折射RenderTexture
    /// </summary>
    private void DrawRefractionRenderTexture(Camera cam)
    {
        CreateObjects(cam, ref _refractionRenderTex, ref _refractionCamera);
        CoreTool.CloneCameraModes(cam, _refractionCamera);

        Vector3 pos = transform.position;
        Vector3 normal = transform.up;

        Matrix4x4 projection = cam.worldToCameraMatrix;
        projection *= Matrix4x4.Scale(new Vector3(1,Mathf.Clamp(1-RefractionAngle,0.001f,1),1));
        _refractionCamera.worldToCameraMatrix = projection;

        Vector4 clipPlane = CoreTool.CameraSpacePlane(_refractionCamera, pos, normal, 1.0f, 0);
        projection = cam.projectionMatrix;
        projection[2] = clipPlane.x + projection[3];//x
        projection[6] = clipPlane.y + projection[7];//y
        projection[10] = clipPlane.z + projection[11];//z
        projection[14] = clipPlane.w + projection[15];//w

        _refractionCamera.projectionMatrix = projection;

        _refractionCamera.cullingMask = ~(1 << 4) & Layers.value; // never render water layer
        _refractionCamera.targetTexture = _refractionRenderTex;       

        _refractionCamera.transform.position = cam.transform.position;
        _refractionCamera.transform.eulerAngles = cam.transform.eulerAngles;
        _refractionCamera.Render();
    }

    void OnDisable()
    {
        if (_reflectionRenderTex)
        {
            DestroyImmediate(_reflectionRenderTex);
            _reflectionRenderTex = null;
        }
        if (_reflectionCamera)
        {
            DestroyImmediate(_reflectionCamera.gameObject);
            _reflectionCamera = null;
        }

        if (_refractionRenderTex)
        {
            DestroyImmediate(_refractionRenderTex);
            _refractionRenderTex = null;
        }
        if (_refractionCamera)
        {
            DestroyImmediate(_refractionCamera.gameObject);
            _refractionCamera = null;
        }
    }

    void CreateObjects(Camera srcCam, ref RenderTexture renderTex, ref Camera destCam)
    {
        // Reflection render texture
        if (!renderTex || _OldTexSize != TexSize)
        {
            if (renderTex)
                DestroyImmediate(renderTex);
            renderTex = new RenderTexture(TexSize, TexSize, 0);
            renderTex.name = "__RefRenderTexture" + renderTex.GetInstanceID();
            renderTex.isPowerOfTwo = true;
            renderTex.hideFlags = HideFlags.DontSave;
            renderTex.antiAliasing = 4;
            renderTex.anisoLevel = 0;
            _OldTexSize = TexSize;
        }

        if (!destCam) // catch both not-in-dictionary and in-dictionary-but-deleted-GO
        {
            GameObject go = new GameObject("__RefCamera for " + srcCam.GetInstanceID(), typeof(Camera), typeof(Skybox));
            destCam = go.camera;
            destCam.enabled = false;
            destCam.transform.position = transform.position;
            destCam.transform.rotation = transform.rotation;
            destCam.gameObject.AddComponent("FlareLayer");
            go.hideFlags = HideFlags.HideAndDontSave;
        }
    }
}

WaterEditor.cs

using UnityEngine;
using System.Collections;
using System;
using UnityEditor;

[CustomEditor(typeof(Water))]
public class WaterEditor : Editor
{

    GUIContent[] _renderTextureOptions = new GUIContent[8] {new GUIContent("16"), new GUIContent("32"), new GUIContent("64"), new GUIContent("128"),
        new GUIContent("256"), new GUIContent("512"), new GUIContent("1024"), new GUIContent("2048") };
    int[] _renderTextureSize = new int[8] { 16, 32, 64, 128, 256, 512, 1024, 2048 };
    public override void OnInspectorGUI()
    {
        Water water = target as Water;
        EditorGUILayout.PropertyField(this.serializedObject.FindProperty("RefType"), new GUIContent("RefType"));
        EditorGUILayout.PropertyField(this.serializedObject.FindProperty("DisablePixelLights"), new GUIContent("DisablePixelLights"));
        EditorGUILayout.PropertyField(this.serializedObject.FindProperty("Layers"), new GUIContent("Layers"));
        EditorGUILayout.IntPopup(this.serializedObject.FindProperty("TexSize"), _renderTextureOptions, _renderTextureSize, new GUIContent("TexSize"));

        if (NGUIEditorTools.DrawHeader("Reflect Settings"))
        {
            NGUIEditorTools.BeginContents();
            {
                EditorGUILayout.Slider(this.serializedObject.FindProperty("ReflectClipPlaneOffset"),0,0.1f,new GUIContent("ClipPlane Offset"));
            }
            NGUIEditorTools.EndContents();
        }

        if (NGUIEditorTools.DrawHeader("Refraction Settings"))
        {
            NGUIEditorTools.BeginContents();
            {
                EditorGUILayout.Slider(this.serializedObject.FindProperty("RefractionAngle"),0,1, new GUIContent("Refraction Angle"));
            }
            NGUIEditorTools.EndContents();
        }
        this.serializedObject.ApplyModifiedProperties();
    }
}

CoreTool.cs

using System.Collections;
using System;
using UnityEngine;

/// <summary>
/// 工具类
/// </summary>
public static class CoreTool
{
    #region Config配置
    /// <summary>
    /// 验证当前文件是否为配置文件
    /// </summary>
    /// <param name="filePath">文件路径</param>
    /// <returns></returns>
    public static bool IsConfig(string filePath)
    {
        return true;
    }
    #endregion

    #region Camera
    /// <summary>
    /// 将源摄像机状态克隆到目标相机
    /// </summary>
    /// <param name="src">源相机</param>
    /// <param name="dest">目标相机</param>
    public static void CloneCameraModes(Camera src, Camera dest)
    {
        if (dest == null)
            return;
        // set camera to clear the same way as current camera
        dest.clearFlags = src.clearFlags;
        dest.backgroundColor = src.backgroundColor;
        if (src.clearFlags == CameraClearFlags.Skybox)
        {
            Skybox sky = src.GetComponent(typeof(Skybox)) as Skybox;
            Skybox mysky = dest.GetComponent(typeof(Skybox)) as Skybox;
            if (!sky || !sky.material)
            {
                mysky.enabled = false;
            }
            else
            {
                mysky.enabled = true;
                mysky.material = sky.material;
            }
        }
        // update other values to match current camera.
        // even if we are supplying custom camera&projection matrices,
        // some of values are used elsewhere (e.g. skybox uses far plane)
        dest.depth = src.depth;
        dest.farClipPlane = src.farClipPlane;
        dest.nearClipPlane = src.nearClipPlane;
        dest.orthographic = src.orthographic;
        dest.fieldOfView = src.fieldOfView;
        dest.aspect = src.aspect;
        dest.orthographicSize = src.orthographicSize;
    }

    /// <summary>
    /// 计算反射矩阵
    /// </summary>
    /// <param name="reflectionMat">原始矩阵</param>
    /// <param name="plane">反射平面</param>
    /// <returns>反射矩阵</returns>
    public static Matrix4x4 CalculateReflectionMatrix(Matrix4x4 reflectionMat, Vector4 plane)
    {
        reflectionMat.m00 = (1F - 2F * plane[0] * plane[0]);
        reflectionMat.m01 = (-2F * plane[0] * plane[1]);
        reflectionMat.m02 = (-2F * plane[0] * plane[2]);
        reflectionMat.m03 = (-2F * plane[3] * plane[0]);

        reflectionMat.m10 = (-2F * plane[1] * plane[0]);
        reflectionMat.m11 = (1F - 2F * plane[1] * plane[1]);
        reflectionMat.m12 = (-2F * plane[1] * plane[2]);
        reflectionMat.m13 = (-2F * plane[3] * plane[1]);

        reflectionMat.m20 = (-2F * plane[2] * plane[0]);
        reflectionMat.m21 = (-2F * plane[2] * plane[1]);
        reflectionMat.m22 = (1F - 2F * plane[2] * plane[2]);
        reflectionMat.m23 = (-2F * plane[3] * plane[2]);

        reflectionMat.m30 = 0F;
        reflectionMat.m31 = 0F;
        reflectionMat.m32 = 0F;
        reflectionMat.m33 = 1F;
        return reflectionMat;
    }

    /// <summary>
    /// 计算指定平面在摄像机中的空间位置
    /// </summary>
    /// <param name="cam">摄像机</param>
    /// <param name="pos">平面上的点</param>
    /// <param name="normal">平面法线</param>
    /// <param name="sideSign">1:平面正面,-1:平面反面</param>
    /// <param name="clipPlaneOffset">平面法线位置偏移量</param>
    /// <returns></returns>
    public static Vector4 CameraSpacePlane(Camera cam, Vector3 pos, Vector3 normal, float sideSign,float clipPlaneOffset)
    {
        Vector3 offsetPos = pos + normal * clipPlaneOffset;
        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));
    }

    /// <summary>
    /// 由剪裁面计算投影倾斜矩阵
    /// </summary>
    /// <param name="projection">投影矩阵</param>
    /// <param name="clipPlane">剪裁面</param>
    /// <param name="sideSign">剪裁平面(-1:平面下面,1:平面上面)</param>
    public static Matrix4x4 CalculateObliqueMatrix(Matrix4x4 projection, Vector4 clipPlane,float sideSign)
    {
        Vector4 q = projection.inverse * new Vector4(
            sgn(clipPlane.x),
            sgn(clipPlane.y),
            1.0f,
            1.0f
        );
        Vector4 c = clipPlane * (2.0F / (Vector4.Dot(clipPlane, q)));
        // third row = clip plane - fourth row
        projection[2] = c.x + Mathf.Sign(sideSign)*projection[3];
        projection[6] = c.y + Mathf.Sign(sideSign) * projection[7];
        projection[10] = c.z + Mathf.Sign(sideSign) * projection[11];
        projection[14] = c.w + Mathf.Sign(sideSign) * projection[15];
        return projection;
    }

    private static float sgn(float a)
    {
        if (a > 0.0f) return 1.0f;
        if (a < 0.0f) return -1.0f;
        return 0.0f;
    }

    /// <summary>
    /// 由水平、垂直距离修改倾斜矩阵
    /// </summary>
    /// <param name="projMatrix">倾斜矩阵</param>
    /// <param name="horizObl">水平方向</param>
    /// <param name="vertObl">垂直方向</param>
    /// <returns>修改后的倾斜矩阵</returns>
    public static Matrix4x4 CalculateObliqueMatrix(Matrix4x4 projMatrix, float horizObl, float vertObl)
    {
        Matrix4x4 mat = projMatrix;
	    mat[0, 2] = horizObl;
	    mat[1, 2] = vertObl;
	    return mat;
    }
    #endregion

    #region Shader Matrix4x4
    /// <summary>
    /// tex2DProj到tex2D的uv纹理转换矩阵
    /// 在shader中,
    /// vert=>o.posProj = mul(_ProjMatrix, v.vertex);
    /// frag=>tex2D(_RefractionTex,float2(i.posProj) / i.posProj.w)
    /// </summary>
    /// <param name="transform">要显示纹理的对象</param>
    /// <param name="cam">当前观察的摄像机</param>
    /// <returns>返回转换矩阵</returns>
    public static Matrix4x4 UV_Tex2DProj2Tex2D(Transform transform,Camera cam)
    {
        Matrix4x4 scaleOffset = Matrix4x4.TRS(
            new Vector3(0.5f, 0.5f, 0.5f), Quaternion.identity, new Vector3(0.5f, 0.5f, 0.5f));
        Vector3 scale = transform.lossyScale;
        Matrix4x4 _ProjMatrix = transform.localToWorldMatrix * Matrix4x4.Scale(new Vector3(1.0f / scale.x, 1.0f / scale.y, 1.0f / scale.z));
        _ProjMatrix = scaleOffset * cam.projectionMatrix * cam.worldToCameraMatrix * _ProjMatrix;
        return _ProjMatrix;
    }
    #endregion
}

Shader

Shader "GameCore/Mobile/Water/Diffuse"
{
    Properties {
        _ReflectionTex ("Reflection", 2D) = "white" {}
		_RefractionTex ("Refraction", 2D) = "white" {}
		_RefColor("Color",Color) = (1,1,1,1)
	}
	SubShader {
        Tags {
            "RenderType"="Opaque"}
		LOD 100
		Pass {
            CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#include "UnityCG.cginc"

			uniform float4x4 _ProjMatrix;
			uniform float _RefType;
            sampler2D _ReflectionTex;
			sampler2D _RefractionTex;
            float4 _RefColor;
            struct outvertex {
                float4 pos : SV_POSITION;
                float4 uv0 : TEXCOORD0;
				float4 refparam : COLOR0;//r:fresnel,g:none,b:none,a:none
            };

			outvertex vert(appdata_tan v) {
                outvertex o;
                o.pos = mul (UNITY_MATRIX_MVP,v.vertex);
                float4 posProj = mul(_ProjMatrix, v.vertex);
				o.uv0 = posProj;
				float3 r =normalize(ObjSpaceViewDir(v.vertex));
				float d = saturate(dot(r,normalize(v.normal)));//r+(1-r)*pow(d,5)
				o.refparam =float4(d,0,0,0);

				return o;
            }

			float4 frag(outvertex i) : COLOR {
				half4 flecol = tex2D(_ReflectionTex,float2(i.uv0) / i.uv0.w);
				half4 fracol = tex2D(_RefractionTex,float2(i.uv0) / i.uv0.w);
				half4 outcolor = half4(1,1,1,1);
				if(_RefType == 0)
				{
					outcolor = lerp(flecol,fracol,i.refparam.r);
				}
				else if(_RefType == 1)
				{
					outcolor = flecol;
				}
				else if(_RefType == 2)
				{
					outcolor = fracol;
				}
                return outcolor*_RefColor;
            }
			ENDCG
		}
	}
}

Unity3d 实时折射与反射

时间: 2024-11-10 15:07:17

Unity3d 实时折射与反射的相关文章

Unity3d 镜面折射 vertex and frag Shader源码

Unity3d 镜面折射 网上能找到的基本上是固定管道或表面渲染的shader, 特此翻译为顶点.片段渲染的Shader, 本源码只涉及shader与cs部分, 请自行下载NGUI unity3d 版本:v4.3.1 RefractionMirror.cs using UnityEngine; using System.Collections; using System; /// <summary> /// 镜面折射效果 /// </summary> [AddComponentMe

Unity 3D 海水的实现2 折射与反射 离屏相机的渲染

版本:unity 5.4.1  语言:C# 在上节博文实现后,我添加了一些场景的元素,海水的效果大概是这个样子的: 接下来的目标是实现海水的折射和反射,书中使用的Unity应该是4.x时代的,Unity自带基础包是5.x的,然后我再在网上看了一个例子,看了下来基本原理都差不多. 还记得移动端简单阴影的实现吧,对,就是添加一个相机把照到的玩家传给Shader后,显示在地上,原理是一样的. 首先获取到玩家的相机,新建相机到玩家当前相机,经过一个反射矩阵的旋转后,截取海平面以上的渲染,然后再将渲染出来

Unity shader实现水效果(折射,反射,波浪,1.菲尼尔,深度颜色)

整个实现过程,包括水面的UV流动,折射,反射,根据深度进行透明值处理等等 原文地址:https://www.cnblogs.com/ubanck/p/9606626.html

Unity3d BTDF实时折射模拟有粗糙度的半透明物体

折射的原理是运用BTDF的一个球形高斯近似 需要考虑折射光的来源,一般会想到用环境贴图(IBL)或者grab texture,但是折射光不全都来自一个平面,所以选择环境贴图来作为折射光.这个效果主要是BTDF计算折射光,和BRDF计算物体的反射光,二者加和即是最终结果. 我们需要用到的参数  我们的最终实现公式为: fr是BRDF,ft是BTDF. Fr 我们首先复习一下BRDF,因为BTDF也要用到BRDF的知识.详细BRDF请看这篇:Unity3d 基于物理渲染Physically-Base

unity3d Hair real time rendering 真实头发实时渲染(转)

惊现塞拉酱 算法是Weta Digital根据siggraph2003的论文加以改进,改进之前使用的是Kajiya and Kay’s 模型,它能量不守恒,也就是说不是基于物理的,不准确 电镜下真实头发丝纤维的照片,我们发现上面有很多重叠的角质层叫做毛小皮也叫毛鳞片,他们相对根部的倾斜角度大约为3°,近似模型如下图 头发纤维的模型R为反射(re?ection),T为穿透(transmission),也就是折射这里假设光有三种传播方式R, TT, TRTR是直接反射,TT是经过两次折射TRT是穿透

unity3d Hair real time rendering 真实头发实时渲染

先放上效果 惊现塞拉酱 算法是Weta Digital根据siggraph2003的论文加以改进,改进之前使用的是Kajiya and Kay's 模型,它能量不守恒,也就是说不是基于物理的,不准确 电镜下真实头发丝纤维的照片,我们发现上面有很多重叠的角质层叫做毛小皮也叫毛鳞片,他们相对根部的倾斜角度大约为3°,近似模型如下图 头发纤维的模型 R为反射(re?ection),T为穿透(transmission),也就是折射 这里假设光有三种传播方式R, TT, TRT R是直接反射, TT是经过

Unity Shaders and Effects Cookbook (4-3)遮罩反射

今天看完了4.3小节,Unity3d 中的遮罩反射,这一节内容比较少. 如果大家对Unity Shader 感兴趣的白菜,那么推荐大家购买一本 Unity 着色器和屏幕特效开发秘笈 .这本书写的浅显易懂,很适合入门. 当看完这本书,大概了解 Shader 的用处的时候,再去看更高级的 Shader 教程也可以. 京东图书连接 http://item.jd.com/1338728578.html 转自http://blog.csdn.net/huutu http://www.thisisgame.

unity3d Human skin real time rendering 真实模拟人皮实时渲染(转)

先放出结果图片...由于网上下的模型是拼的,所以眼皮,脸颊,嘴唇看起来像 存在裂痕,解决方式是加入曲面细分和置换贴图 进行一定隆起,但是博主试了一下fragment shader的曲面细分,虽然细分成功了但是着色效果变的很奇怪,这里就不用曲面细分了,大家如果有在fragment shader上用曲面细分的好办法,可以的话请告诉我 参数设置1 参数设置2 细致到毛孔的高光 次表面散射的耳朵 人皮渲染是十多年的课题了,人们想尽一切办法想让其变得真实可信,大型3A级次时代游戏近来做的又来越真实了如<罗

unity3d Human skin real time rendering 真实模拟人皮实时渲染

先放出结果图片...由于网上下的模型是拼的,所以眼皮,脸颊,嘴唇看起来像存在裂痕,解决方式是加入曲面细分和置换贴图进行一定隆起,但是博主试了一下fragment shader的曲面细分,虽然细分成功了但是着色效果变的很奇怪,这里就不用曲面细分了,大家如果有在fragment shader上用曲面细分的好办法,可以的话请告诉我 参数设置1 参数设置2 细致到毛孔的高光 次表面散射的耳朵 人皮渲染是十多年的课题了,人们想尽一切办法想让其变得真实可信,大型3A级次时代游戏近来做的又来越真实了如<罗马之