海洋数学知识笔记

 对于无限海面应该有几种做法,第一种是根据你所在位置动态加载海面,无缝拼接。第二种是玩家移动范围在某一个区域,那么只要按照下图这种回字形,根据距离设置LOD就好。第一种比较简单,现在主要看下第二种的做法。

   这是LOD的最重要的枚举了

   enum PatchType
    {
        /// <summary>
       最核心部分,2x2的平面
        ///
        ///    1 -------
        ///      |  |  |
        ///  z   -------
        ///      |  |  |
        ///    0 -------
        ///      0     1
        ///         x
        ///
        Interior,

        /// <summary>
       增加外围一圈,构成4x4的完整平面
        ///
        ///      -------------
        ///      |  |  |  |  |
        ///    1 -------------
        ///      |  |  |  |  |
        ///  z   -------------
        ///      |  |  |  |  |
        ///    0 -------------
        ///      |  |  |  |  |
        ///      -------------
        ///         0     1
        ///            x
        ///
        /// </summary>
        Fat,

        /// <summary>
      增加右边,构成2x3的平面
        ///
        ///    1 ----------
        ///      |  |  |  |
        ///  z   ----------
        ///      |  |  |  |
        ///    0 ----------
        ///      0     1
        ///         x
        ///
        /// </summary>
        FatX,

        /// <summary>
        增加右边,减少顶部,构成1x3的平面
        /// </summary>
        FatXSlimZ,

        /// <summary>
        增加右边,这个右边特别远,直接触及边界
        ///
        ///    1 --------------------------------------------------------------------------------------
        ///      |  |  |                                                                              |
        ///  z   --------------------------------------------------------------------------------------
        ///      |  |  |                                                                              |
        ///    0 --------------------------------------------------------------------------------------
        ///      0     1
        ///         x
        ///
        /// </summary>
        FatXOuter,

        /// <summary>

        增加顶部和右边,构成3x3的边界
        /// </summary>
        FatXZ,

        /// <summary>

       增加顶部和右边并且是无限远的边界
        /// Adds skirts at the top and right sides of the patch and pushes them to horizon
        /// </summary>
        FatXZOuter,

        /// <summary>
        在x方向少一组顶点
        /// </summary>
        SlimX,

        /// <summary>
        在x和z方向都少一组顶点
        /// </summary>
        SlimXZ,

        /// <summary>
        /// x方向少一组顶点,z方向多一组顶点
        ///
        ///      ----
        ///      |  |
        ///    1 ----
        ///      |  |
        ///  z   ----
        ///      |  |
        ///    0 ----
        ///      0     1
        ///         x
        ///
        /// </summary>
        SlimXFatZ,

        /// <summary>
        /// Number of patch types
        /// </summary>
        Count,
    }

 

 static Mesh BuildOceanPatch(PatchType pt, float baseVertDensity)
    {
        ArrayList verts = new ArrayList();
        ArrayList indices = new ArrayList();

        float dx = 1f / baseVertDensity;

        float skirtXminus = 0f, skirtXplus = 0f;
        float skirtZminus = 0f, skirtZplus = 0f;
        // 如果是完美扩展,就全部+1
        if (pt == PatchType.Fat) { skirtXminus = skirtXplus = skirtZminus = skirtZplus = 1f; }
         //如果x扩展,那么x+1
        else if (pt == PatchType.FatX || pt == PatchType.FatXOuter) { skirtXplus = 1f; }
         //如果xz扩展,xz扩展+1
        else if (pt == PatchType.FatXZ || pt == PatchType.FatXZOuter) { skirtXplus = skirtZplus = 1f; }
        else if (pt == PatchType.FatXSlimZ) { skirtXplus = 1f; skirtZplus = -1f; }
        else if (pt == PatchType.SlimX) { skirtXplus = -1f; }
        else if (pt == PatchType.SlimXZ) { skirtXplus = skirtZplus = -1f; }
        else if (pt == PatchType.SlimXFatZ) { skirtXplus = -1f; skirtZplus = 1f; }

        float sideLength_verts_x = 1f + baseVertDensity + skirtXminus + skirtXplus;
        float sideLength_verts_z = 1f + baseVertDensity + skirtZminus + skirtZplus;

        float start_x = -0.5f - skirtXminus * dx;
        float start_z = -0.5f - skirtZminus * dx;
        float end_x = 0.5f + skirtXplus * dx;
        float end_z = 0.5f + skirtZplus * dx;

        for (float j = 0; j < sideLength_verts_z; j++)
        {
            // interpolate z across patch
            float z = Mathf.Lerp(start_z, end_z, j / (sideLength_verts_z - 1f));

            // push outermost edge out to horizon
            if (pt == PatchType.FatXZOuter && j == sideLength_verts_z - 1f)
                z *= 100f;

            for (float i = 0; i < sideLength_verts_x; i++)
            {
                // interpolate x across patch
                float x = Mathf.Lerp(start_x, end_x, i / (sideLength_verts_x - 1f));

                // push outermost edge out to horizon
                if (i == sideLength_verts_x - 1f && (pt == PatchType.FatXOuter || pt == PatchType.FatXZOuter))
                    x *= 100f;

                // could store something in y, although keep in mind this is a shared mesh that is shared across multiple lods
                verts.Add(new Vector3(x, 0f, z));
            }
        }


        //
        // indices

        int sideLength_squares_x = (int)sideLength_verts_x - 1;
        int sideLength_squares_z = (int)sideLength_verts_z - 1;

        for (int j = 0; j < sideLength_squares_z; j++)
        {
            for (int i = 0; i < sideLength_squares_x; i++)
            {
                bool flipEdge = false;

                if (i % 2 == 1) flipEdge = !flipEdge;
                if (j % 2 == 1) flipEdge = !flipEdge;

                int i0 = i + j * (sideLength_squares_x + 1);
                int i1 = i0 + 1;
                int i2 = i0 + (sideLength_squares_x + 1);
                int i3 = i2 + 1;

                if (!flipEdge)
                {
                    // tri 1
                    indices.Add(i3);
                    indices.Add(i1);
                    indices.Add(i0);

                    // tri 2
                    indices.Add(i0);
                    indices.Add(i2);
                    indices.Add(i3);
                }
                else
                {
                    // tri 1
                    indices.Add(i3);
                    indices.Add(i1);
                    indices.Add(i2);

                    // tri 2
                    indices.Add(i0);
                    indices.Add(i2);
                    indices.Add(i1);
                }
            }
        }


        //
        // create mesh

        Mesh mesh = new Mesh();
        if (verts != null && verts.Count > 0)
        {
            Vector3[] arrV = new Vector3[verts.Count];
            verts.CopyTo(arrV);

            int[] arrI = new int[indices.Count];
            indices.CopyTo(arrI);

            mesh.SetIndices(null, MeshTopology.Triangles, 0);
            mesh.vertices = arrV;
            mesh.normals = null;
            mesh.SetIndices(arrI, MeshTopology.Triangles, 0);

            // recalculate bounds. add a little allowance for snapping. in the chunk renderer script, the bounds will be expanded further
            // to allow for horizontal displacement
            mesh.RecalculateBounds();
            Bounds bounds = mesh.bounds;
            bounds.extents = new Vector3(bounds.extents.x + dx, 100f, bounds.extents.z + dx);
            mesh.bounds = bounds;
            mesh.name = pt.ToString();
        }
        return mesh;
    }

 

float ComputeLodAlpha(float3 i_worldPos, float i_meshScaleAlpha)
{
	// taxicab distance from ocean center drives LOD transitions
	float2 offsetFromCenter = float2(abs(i_worldPos.x - _OceanCenterPosWorld.x), abs(i_worldPos.z - _OceanCenterPosWorld.z));
	float taxicab_norm = max(offsetFromCenter.x, offsetFromCenter.y);
        
        //当前lod的缩放,假设缩放为1,边长就是4,而taxicab最多就是2,那么如果是2,那么lodalpha就是1,如果是0,就是-1
	// interpolation factor to next lod (lower density / higher sampling period)
	float lodAlpha = taxicab_norm / _LD_Pos_Scale_0.z - 1.0;

	// lod alpha is remapped to ensure patches weld together properly. patches can vary significantly in shape (with
	// strips added and removed), and this variance depends on the base density of the mesh, as this defines the strip width.
	// using .15 as black and .85 as white should work for base mesh density as low as 16.
	const float BLACK_POINT = 0.15, WHITE_POINT = 0.85;
        lodalpha在大于0的部分才有意义,就是外围的格子,需要无缝衔接,lodalpha逐渐增大,如果视野增高,需要缩放,lodalpha也要变大。这里有点奇怪需要再看看这个是干嘛的。
        现在明白了,摄像机越高,lod会放大,本来1m的格子会瞬间变成2m,所以通过这个可以平滑1-2m之间的过度,而完成放大之后,这个值会被重置为0,非常平滑
	lodAlpha = max((lodAlpha - BLACK_POINT) / (WHITE_POINT - BLACK_POINT), 0.);

	// blend out lod0 when viewpoint gains altitude
	lodAlpha = min(lodAlpha + i_meshScaleAlpha, 1.);

#if _DEBUGDISABLESMOOTHLOD_ON
	lodAlpha = 0.;
#endif

	return lodAlpha;
}


void SnapAndTransitionVertLayout(float i_meshScaleAlpha, inout float3 io_worldPos, out float o_lodAlpha)
{
	//x是每个方块的大小
	const float SQUARE_SIZE_2 = 2.0*_GeomData.x, SQUARE_SIZE_4 = 4.0*_GeomData.x;

	// snap the verts to the grid
	橡皮擦必须两倍擦除,用来保证边缘分布的一致性,
        世界坐标偏移,矫正世界坐标xz,这的原因在于顶点着色器通过worldpos来进行位置变化,但由于顶点数不够多,如果顶点发生水平移动,那么即便worldpos算出来结果一样,但插值后也不会产生想要的结果,所以这是必须擦除的原因。
	io_worldPos.xz -= frac(unity_ObjectToWorld._m03_m23 / SQUARE_SIZE_2) * SQUARE_SIZE_2; // caution - sign of frac might change in non-hlsl shaders

	// compute lod transition alpha
	o_lodAlpha = ComputeLodAlpha(io_worldPos, i_meshScaleAlpha);

	// now smoothly transition vert layouts between lod levels - move interior verts inwards towards center
	float2 m = frac(io_worldPos.xz / SQUARE_SIZE_4); // this always returns positive
        这里四倍的格子,取余,就是可以获得0-4个格子的余数,负数也是一样,m-0.5,那么就把这4个格子范围变成[-0.5,0.5],这里其实是将边缘部分格子重叠,相当于内部是2x倍的格子,边缘必须是1x倍,才能和+1的lod完全重叠,没有接缝。
	float2 offset = m - 0.5;
	// check if vert is within one square from the center point which the verts move towards
	const float minRadius = 0.26; //0.26 is 0.25 plus a small "epsilon" - should solve numerical issues
	if (abs(offset.x) < minRadius) io_worldPos.x += offset.x * o_lodAlpha * SQUARE_SIZE_4;
	if (abs(offset.y) < minRadius) io_worldPos.z += offset.y * o_lodAlpha * SQUARE_SIZE_4;
}

这里有一个概念要理解,就是随着摄像机高度增加,scale放大,其实就是lod从1过渡到2的过程,因为1是边长为1,放大2倍后,边长变成2了,而这刚好是下一个lod的边长。所以权重也类似,随着高度升高,权重要向外围看齐。最外围特殊一点,因为最外围可能会消失,那么最外围出在临界值的时候,最外圈权重其实要再缩小一些。
float wt_0 = (1. - lodAlpha) * _LD_Params_0.z;
float wt_1 = (1. - wt_0) * _LD_Params_1.z;
void SampleDisplacementsNormals(in sampler2D i_dispSampler, in float2 i_uv, in float i_wt, in float i_invRes, in float i_texelSize, inout float3 io_worldPos, inout half2 io_nxz)
{
	const float4 uv = float4(i_uv, 0., 0.);
        //从波浪贴图里面采样
	const half3 disp = tex2Dlod(i_dispSampler, uv).xyz;
	io_worldPos += i_wt * disp;

	float3 n; {
                 在x方向上偏移一个像素采样,同时加上一个格子,在z也做同样的处理,得到两个位置,减去原来的位置,就得到两条垂直的向量,cross一下就得到了新法线。
                这里需要图形学画一下,根据x和z偏移得到的向量,不能够直接叉乘,而要分别加上偏移,画一下图就懂了
		float3 dd = float3(i_invRes, 0.0, i_texelSize);
		half3 disp_x = dd.zyy + tex2Dlod(i_dispSampler, uv + dd.xyyy).xyz;
		half3 disp_z = dd.yyz + tex2Dlod(i_dispSampler, uv + dd.yxyy).xyz;
		n = normalize(cross(disp_z - disp, disp_x - disp));
	}
	io_nxz += i_wt * n.xz;
}


void SampleFoam(in sampler2D i_oceanFoamSampler, float2 i_uv, in float i_wt, inout half io_foam)
{

	io_foam += i_wt * tex2Dlod(i_oceanFoamSampler, float4(i_uv, 0., 0.)).x;
}


void SampleFlow(in sampler2D i_oceanFlowSampler, float2 i_uv, in float i_wt, inout half2 io_flow)
{
	const float4 uv = float4(i_uv, 0., 0.);
	io_flow += i_wt * tex2Dlod(i_oceanFlowSampler, uv).xy;
}


void SampleSeaFloorHeightAboveBaseline(in sampler2D i_oceanDepthSampler, float2 i_uv, in float i_wt, inout half io_oceanDepth)
{
	io_oceanDepth += i_wt * (tex2Dlod(i_oceanDepthSampler, float4(i_uv, 0., 0.)).x);
}

void SampleShadow(in sampler2D i_oceanShadowSampler, float2 i_uv, in float i_wt, inout fixed2 io_shadow)
{
	io_shadow += i_wt * tex2Dlod(i_oceanShadowSampler, float4(i_uv, 0., 0.)).xy;
}
half2 SampleNormalMaps(float2 worldXZUndisplaced, float lodAlpha)
{
	const float2 v0 = float2(0.94, 0.34), v1 = float2(-0.85, -0.53);
	const float geomSquareSize = _GeomData.x;
	float nstretch = _NormalsScale * geomSquareSize; // normals scaled with geometry
	const float spdmulL = _GeomData.y;
	half2 norm =
		UnpackNormal(tex2D(_Normals, (v0*_CrestTime*spdmulL + worldXZUndisplaced) / nstretch)).xy +
		UnpackNormal(tex2D(_Normals, (v1*_CrestTime*spdmulL + worldXZUndisplaced) / nstretch)).xy;

	// blend in next higher scale of normals to obtain continuity
	const float farNormalsWeight = _InstanceData.y;
	const half nblend = lodAlpha * farNormalsWeight;
	if (nblend > 0.001)
	{
		// next lod level
		nstretch *= 2.;
		const float spdmulH = _GeomData.z;
		norm = lerp(norm,
			UnpackNormal(tex2D(_Normals, (v0*_CrestTime*spdmulH + worldXZUndisplaced) / nstretch)).xy +
			UnpackNormal(tex2D(_Normals, (v1*_CrestTime*spdmulH + worldXZUndisplaced) / nstretch)).xy,
			nblend);
	}

	// approximate combine of normals. would be better if normals applied in local frame.
	return _NormalsStrength * norm;
}

void ApplyNormalMapsWithFlow(float2 worldXZUndisplaced, float2 flow, float lodAlpha, inout half3 io_n)
{
	const float half_period = 1;
	const float period = half_period * 2;
	float sample1_offset = fmod(_CrestTime, period);
	float sample1_weight = sample1_offset / half_period;
	if (sample1_weight > 1.0) sample1_weight = 2.0 - sample1_weight;
	float sample2_offset = fmod(_CrestTime + half_period, period);
	float sample2_weight = 1.0 - sample1_weight;

	// In order to prevent flow from distorting the UVs too much,
	// we fade between two samples of normal maps so that for each
	// sample the UVs can be reset
	half2 io_n_1 = SampleNormalMaps(worldXZUndisplaced - (flow * sample1_offset), lodAlpha);
	half2 io_n_2 = SampleNormalMaps(worldXZUndisplaced - (flow * sample2_offset), lodAlpha);
	io_n.xz += sample1_weight * io_n_1;
	io_n.xz += sample2_weight * io_n_2;
	io_n = normalize(io_n);
}
half WhiteFoamTexture(half i_foam, float2 i_worldXZUndisplaced)
{
	half ft = lerp(
		tex2D(_FoamTexture, (1.25*i_worldXZUndisplaced + _CrestTime / 10.) / _FoamScale).r,
		tex2D(_FoamTexture, (3.00*i_worldXZUndisplaced - _CrestTime / 10.) / _FoamScale).r,
		0.5);

	// black point fade
	i_foam = saturate(1. - i_foam);
	return smoothstep(i_foam, i_foam + _WaveFoamFeather, ft);
}

void ComputeFoam(half i_foam, float2 i_worldXZUndisplaced, float2 i_worldXZ, half3 i_n, float i_pixelZ, float i_sceneZ, half3 i_view, float3 i_lightDir, half i_shadow, out half3 o_bubbleCol, out half4 o_whiteFoamCol)
{
	half foamAmount = i_foam;

	// feather foam very close to shore
	foamAmount *= saturate((i_sceneZ - i_pixelZ) / _ShorelineFoamMinDepth);

	// Additive underwater foam - use same foam texture but add mip bias to blur for free
	float2 foamUVBubbles = (lerp(i_worldXZUndisplaced, i_worldXZ, 0.05) + 0.5 * _CrestTime * _WindDirXZ) / _FoamScale + 0.125 * i_n.xz;
	half bubbleFoamTexValue = tex2Dlod(_FoamTexture, float4(.74 * foamUVBubbles - _FoamBubbleParallax * i_view.xz / i_view.y, 0., 5.)).r;
	o_bubbleCol = (half3)bubbleFoamTexValue * _FoamBubbleColor.rgb * saturate(i_foam * _WaveFoamBubblesCoverage) * AmbientLight();

	// White foam on top, with black-point fading
	half whiteFoam = WhiteFoamTexture(foamAmount, i_worldXZUndisplaced);

#if _FOAM3DLIGHTING_ON
	// Scale up delta by Z - keeps 3d look better at distance. better way to do this?
	float2 dd = float2(0.25 * i_pixelZ * _FoamTexture_TexelSize.x, 0.);
	half whiteFoam_x = WhiteFoamTexture(foamAmount, i_worldXZUndisplaced + dd.xy);
	half whiteFoam_z = WhiteFoamTexture(foamAmount, i_worldXZUndisplaced + dd.yx);

	// compute a foam normal
	half dfdx = whiteFoam_x - whiteFoam, dfdz = whiteFoam_z - whiteFoam;
	half3 fN = normalize(i_n + _WaveFoamNormalStrength * half3(-dfdx, 0., -dfdz));
	// do simple NdL and phong lighting
	half foamNdL = max(0., dot(fN, i_lightDir));
	o_whiteFoamCol.rgb = _FoamWhiteColor.rgb * (AmbientLight() + _WaveFoamLightScale * _LightColor0 * foamNdL * i_shadow);
	half3 refl = reflect(-i_view, fN);
	o_whiteFoamCol.rgb += pow(max(0., dot(refl, i_lightDir)), _WaveFoamSpecularFallOff) * _WaveFoamSpecularBoost * _LightColor0 * i_shadow;
#else // _FOAM3DLIGHTING_ON
	o_whiteFoamCol.rgb = _FoamWhiteColor.rgb * (AmbientLight() + _WaveFoamLightScale * _LightColor0 * i_shadow);
#endif // _FOAM3DLIGHTING_ON

	o_whiteFoamCol.a = _FoamWhiteColor.a * whiteFoam;
}
void ComputeFoamWithFlow(half2 flow, half i_foam, float2 i_worldXZUndisplaced, float2 i_worldXZ, half3 i_n, float i_pixelZ, float i_sceneZ, half3 i_view, float3 i_lightDir, half i_shadow, out half3 o_bubbleCol, out half4 o_whiteFoamCol)
{
	const float half_period = 1;
	const float period = half_period * 2;
	float sample1_offset = fmod(_CrestTime, period);
	float sample1_weight = sample1_offset / half_period;
	if (sample1_weight > 1.0) sample1_weight = 2.0 - sample1_weight;
	float sample2_offset = fmod(_CrestTime + half_period, period);
	float sample2_weight = 1.0 - sample1_weight;

	// In order to prevent flow from distorting the UVs too much,
	// we fade between two samples of normal maps so that for each
	// sample the UVs can be reset
	half3 o_bubbleCol1 = half3(0, 0, 0);
	half4 o_whiteFoamCol1 = half4(0, 0, 0, 0);
	half3 o_bubbleCol2 = half3(0, 0, 0);
	half4 o_whiteFoamCol2 = half4(0, 0, 0, 0);

	ComputeFoam(i_foam, i_worldXZUndisplaced - (flow * sample1_offset), i_worldXZ, i_n, i_pixelZ, i_sceneZ, i_view, i_lightDir, i_shadow, o_bubbleCol1, o_whiteFoamCol1);
	ComputeFoam(i_foam, i_worldXZUndisplaced - (flow * sample2_offset), i_worldXZ, i_n, i_pixelZ, i_sceneZ, i_view, i_lightDir, i_shadow, o_bubbleCol2, o_whiteFoamCol2);
	o_bubbleCol = (sample1_weight * o_bubbleCol1) + (sample2_weight * o_bubbleCol2);
	o_whiteFoamCol = (sample1_weight * o_whiteFoamCol1) + (sample2_weight * o_whiteFoamCol2);
}

 

half3 ScatterColour(
	in const float3 i_surfaceWorldPos, in const half i_surfaceOceanDepth, in const float3 i_cameraPos,
	in const half3 i_lightDir, in const half3 i_view, in const fixed i_shadow,
	in const bool i_underWater, in const bool i_outscatterLight)
{
	half depth;
	half waveHeight;
	half shadow = 0.;
	if (i_underWater)
	{
		// compute scatter colour from cam pos. two scenarios this can be called:
		// 1. rendering ocean surface from bottom, in which case the surface may be some distance away. use the scatter
		//    colour at the camera, not at the surface, to make sure its consistent.
		// 2. for the underwater skirt geometry, we don't have the lod data sampled from the verts with lod transitions etc,
		//    so just approximate by sampling at the camera position.
		// this used to sample LOD1 but that doesnt work in last LOD, the data will be missing.
		const float2 uv_0 = LD_0_WorldToUV(i_cameraPos.xz);
		float seaFloorHeightAboveBaseline = 0.;
		SampleSeaFloorHeightAboveBaseline(_LD_Sampler_SeaFloorDepth_0, uv_0, 1.0, seaFloorHeightAboveBaseline);
		depth = DEPTH_BASELINE - seaFloorHeightAboveBaseline;
		waveHeight = 0.;

		fixed2 shadowSoftHard = 0.;
		SampleShadow(_LD_Sampler_Shadow_0, uv_0, 1.0, shadowSoftHard);
		shadow = 1. - shadowSoftHard.x;
	}
	else
	{
		// above water - take data from geometry
		depth = i_surfaceOceanDepth;
		waveHeight = i_surfaceWorldPos.y - _OceanCenterPosWorld.y;
		shadow = i_shadow;
	}

	// base colour
	half3 col = _Diffuse;

#if _SHADOWS_ON
	col = lerp(_DiffuseShadow, col, shadow);
#endif

#if _SUBSURFACESCATTERING_ON
	{
#if _SUBSURFACESHALLOWCOLOUR_ON
		float shallowness = pow(1. - saturate(depth / _SubSurfaceDepthMax), _SubSurfaceDepthPower);
		half3 shallowCol = _SubSurfaceShallowCol;
#if _SHADOWS_ON
		shallowCol = lerp(_SubSurfaceShallowColShadow, shallowCol, shadow);
#endif
		col = lerp(col, shallowCol, shallowness);
#endif

#if _SUBSURFACEHEIGHTLERP_ON
		col += pow(saturate(0.5 + 2.0 * waveHeight / _SubSurfaceHeightMax), _SubSurfaceHeightPower) * _SubSurfaceCrestColour.rgb;
#endif

		// light
		// use the constant term (0th order) of SH stuff - this is the average. it seems to give the right kind of colour
		col *= half3(unity_SHAr.w, unity_SHAg.w, unity_SHAb.w);

		// Approximate subsurface scattering - add light when surface faces viewer. Use geometry normal - don't need high freqs.
		half towardsSun = pow(max(0., dot(i_lightDir, -i_view)), _SubSurfaceSunFallOff);
		col += (_SubSurfaceBase + _SubSurfaceSun * towardsSun) * _SubSurfaceColour.rgb * _LightColor0 * shadow;
	}
#endif // _SUBSURFACESCATTERING_ON

	// outscatter light - attenuate the final colour by the camera depth under the water, to approximate less
	// throughput due to light scatter as the camera gets further under water.
	if (i_outscatterLight)
	{
		half camDepth = i_surfaceWorldPos.y - _WorldSpaceCameraPos.y;
		if (i_underWater)
		{
			col *= exp(-_DepthFogDensity.xyz * camDepth * DEPTH_OUTSCATTER_CONSTANT);
		}
	}

	return col;
}

 


void ApplyReflectionSky(half3 view, half3 n_pixel, half3 lightDir, half shadow, half4 i_screenPos, inout half3 col)
{
	// Reflection
	half3 refl = reflect(-view, n_pixel);
	half3 skyColour;

#if _PLANARREFLECTIONS_ON
	skyColour = PlanarReflection(refl, i_screenPos, n_pixel);
#elif _PROCEDURALSKY_ON
	skyColour = SkyProceduralDP(refl, lightDir);
#else
	skyColour = texCUBE(_Skybox, refl).rgb;
#endif

	// Add primary light to boost it
#if _COMPUTEDIRECTIONALLIGHT_ON
	skyColour += pow(max(0., dot(refl, lightDir)), _DirectionalLightFallOff) * _DirectionalLightBoost * _LightColor0 * shadow;
#endif

	// Fresnel
	const float IOR_AIR = 1.0;
	const float IOR_WATER = 1.33;
	// reflectance at facing angle
	float R_0 = (IOR_AIR - IOR_WATER) / (IOR_AIR + IOR_WATER); R_0 *= R_0;
	// schlick's approximation
	float R_theta = R_0 + (1.0 - R_0) * pow(1.0 - max(dot(n_pixel, view), 0.), _FresnelPower);
	col = lerp(col, skyColour, R_theta);
}
void LateUpdateScale()
{
        // reach maximum detail at slightly below sea level. this should combat cases where visual range can be lost
        // when water height is low and camera is suspended in air. i tried a scheme where it was based on difference
        // to water height but this does help with the problem of horizontal range getting limited at bad times.
        float maxDetailY = SeaLevel - _maxVertDispFromShape / 5f;
        // scale ocean mesh based on camera distance to sea level, to keep uniform detail.
        float camY = Mathf.Max(Mathf.Abs(_viewpoint.position.y) - maxDetailY, 0f);
        这是摄像机对于水面的高度差
        const float HEIGHT_LOD_MUL = 2f;
        float level = camY * HEIGHT_LOD_MUL;
        
        level = Mathf.Max(level, _minScale);
        if (_maxScale != -1f) level = Mathf.Min(level, 1.99f * _maxScale);

        高度差是2的几次方
        float l2 = Mathf.Log(level) / Mathf.Log(2f);
        float l2f = Mathf.Floor(l2);
        这个是小数部分,就是在缩放两级之间,所谓的blend的效果,这样子在摄像机上下的时候,才不会出现闪烁的情况。 缩放物体并且将这个blend值丢给shader
        _viewerAltitudeLevelAlpha = l2 - l2f;

        float newScale = Mathf.Pow(2f, l2f);
        transform.localScale = new Vector3(newScale, 1f, newScale);

        Shader.SetGlobalFloat("_ViewerAltitudeLevelAlpha", _viewerAltitudeLevelAlpha);
}

// blend LOD 0 shape in/out to avoid pop, if the ocean might scale up later (it is smaller than its maximum scale)
        bool needToBlendOutShape = _lodIndex == 0 && Ocean.Instance.ScaleCouldIncrease;
        float meshScaleLerp = needToBlendOutShape ? Ocean.Instance.ViewerAltitudeLevelAlpha : 0f;

最中心的lod取到那个blend的值
        // blend furthest normals scale in/out to avoid pop, if scale could reduce
        bool needToBlendOutNormals = _lodIndex == _totalLodCount - 1 && Ocean.Instance.ScaleCouldDecrease;
        float farNormalsWeight = needToBlendOutNormals ? Ocean.Instance.ViewerAltitudeLevelAlpha : 1f;
而最远处的lod如果缩小,那么也需要取blend进行修改,防止闪烁,中间那一片暂时不考虑。
        _mpb.SetVector("_InstanceData", new Vector4(meshScaleLerp, farNormalsWeight, _lodIndex));
        // geometry data
        // compute grid size of geometry. take the long way to get there - make sure we land exactly on a power of two
        // and not inherit any of the lossy-ness from lossyScale.
        确保缩放是2的n次方,得到每一个方块的边长,滚动速度用的是魔数,看的合理就好
        float squareSize = Mathf.Pow(2f, Mathf.Round(Mathf.Log(transform.lossyScale.x) / Mathf.Log(2f))) / _baseVertDensity;
        float mul = 1.875f; // fudge 1
        float pow = 1.4f; // fudge 2
        float normalScrollSpeed0 = Mathf.Pow(Mathf.Log(1f + 2f * squareSize) * mul, pow);
        float normalScrollSpeed1 = Mathf.Pow(Mathf.Log(1f + 4f * squareSize) * mul, pow);
        _mpb.SetVector("_GeomData", new Vector3(squareSize, normalScrollSpeed0, normalScrollSpeed1));
 public void UpdateTransform()
    {
        if (_transformUpdateFrame == Time.frameCount)
            return;

        _transformUpdateFrame = Time.frameCount;

        _renderDataPrevFrame = _renderData;

        float camOrthSize = 2f * transform.lossyScale.x;
        摄像机看的是4x4的格子,每个格子64个方格,像素当做256,也就是1个像素一个格子
        // find snap period
        _renderData._textureRes = Ocean.Instance.LodDataResolution;
        texelWidth这里就是1/64
        _renderData._texelWidth = 2f * camOrthSize / _renderData._textureRes;
        // snap so that shape texels are stationary
        当物体移动的时候,为了防止闪烁,需要做边缘擦除,这里按照1/64为基础进行取余。
        在shader里面,是对方格的两倍进行取余,我们得到的是该渲染点擦除后的世界坐标
        而c#里,是整个海面的坐标擦除。擦除值也不同,shader里,擦了2/64,也就是1/32
        而c#里,是按照1/64擦除,也就是1个格子一个格子处理。并没有保证三角形的分布。
        _renderData._posSnapped = transform.position
            - new Vector3(Mathf.Repeat(transform.position.x, _renderData._texelWidth), 0f, Mathf.Repeat(transform.position.z, _renderData._texelWidth));

        _renderData._frame = Time.frameCount;

        // detect first update and populate the render data if so - otherwise it can give divide by 0s and other nastiness
        if (_renderDataPrevFrame._textureRes == 0f)
        {
            _renderDataPrevFrame._posSnapped = _renderData._posSnapped;
            _renderDataPrevFrame._texelWidth = _renderData._texelWidth;
            _renderDataPrevFrame._textureRes = _renderData._textureRes;
        }

        _worldToCameraMatrix = CalculateWorldToCameraMatrixRHS(_renderData._posSnapped + Vector3.up * 100f, Quaternion.AngleAxis(90f, Vector3.right));

        _projectionMatrix = Matrix4x4.Ortho(-2f * transform.lossyScale.x, 2f * transform.lossyScale.x, -2f * transform.lossyScale.z, 2f * transform.lossyScale.z, 1f, 500f);
    }


 properties.SetVector(LodTransform.ParamIdPosScale(shapeSlot), new Vector3(renderData._posSnapped.x, renderData._posSnapped.z, lt.transform.lossyScale.x));
half3 OceanEmission(in const half3 i_view, in const half3 i_n_pixel, in const float3 i_lightDir,
					in const half4 i_grabPos, in const float i_pixelZ, in const half2 i_uvDepth, in const float i_sceneZ, in const float i_sceneZ01,
					in const half3 i_bubbleCol, in sampler2D i_normals, in sampler2D i_cameraDepths, in const bool i_underwater, in const half3 i_scatterCol)
				{
					half3 col = i_scatterCol;

					// underwater bubbles reflect in light
					col += i_bubbleCol;

#if _TRANSPARENCY_ON

					// View ray intersects geometry surface either above or below ocean surface

					const half2 uvBackground = i_grabPos.xy / i_grabPos.w;
					half2 uvBackgroundRefract = uvBackground + _RefractionStrength * i_n_pixel.xz;
					half3 sceneColour;
					half3 alpha = 0.;
					float depthFogDistance;

					// Depth fog & caustics - only if view ray starts from above water
					if (!i_underwater)
					{
						const half2 uvDepthRefract = i_uvDepth + _RefractionStrength * i_n_pixel.xz;
						const float sceneZRefract = LinearEyeDepth(tex2D(i_cameraDepths, uvDepthRefract).x);

						// Compute depth fog alpha based on refracted position if it landed on an underwater surface, or on unrefracted depth otherwise
						if (sceneZRefract > i_pixelZ)
						{
							depthFogDistance = sceneZRefract - i_pixelZ;
						}
						else
						{
							depthFogDistance = i_sceneZ - i_pixelZ;

							// We have refracted onto a surface in front of the water. Cancel the refraction offset.
							uvBackgroundRefract = uvBackground;
						}

						sceneColour = tex2D(_BackgroundTexture, uvBackgroundRefract).rgb;
#if _CAUSTICS_ON
						ApplyCaustics(i_view, i_lightDir, i_sceneZ, i_normals, sceneColour);
#endif
					}
					else
					{
						sceneColour = tex2D(_BackgroundTexture, uvBackgroundRefract).rgb;
						depthFogDistance = i_pixelZ;
					}

					alpha = 1. - exp(-_DepthFogDensity.xyz * depthFogDistance);

					// blend from water colour to the scene colour
					col = lerp(sceneColour, col, alpha);

#endif // _TRANSPARENCY_ON

					return col;
				}

 

海洋波形推倒网址:

https://developer.nvidia.com/gpugems/GPUGems/gpugems_ch01.html

 

 

 

 

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付 9.90元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值