浏览代码

Merge pull request #378 from EvgeniiG/master

Remove the sky mesh which caused a memory leak
/sample_game
GitHub 7 年前
当前提交
0547ff04
共有 10 个文件被更改,包括 145 次插入149 次删除
  1. 1
      Assets/ScriptableRenderPipeline/HDRenderPipeline/HDShaderIDs.cs
  2. 43
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Sky/GGXConvolve.shader
  3. 6
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Sky/HDRISky/HDRISkyRenderer.cs
  4. 16
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Sky/HDRISky/Resources/SkyHDRI.shader
  5. 4
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Sky/ProceduralSky/ProceduralSkyRenderer.cs
  6. 17
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Sky/ProceduralSky/Resources/SkyProcedural.shader
  7. 23
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Sky/RuntimeFilterIBL.cs
  8. 173
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Sky/SkyManager.cs
  9. 7
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Utilities.cs
  10. 4
      Assets/ScriptableRenderPipeline/ShaderLibrary/Common.hlsl

1
Assets/ScriptableRenderPipeline/HDRenderPipeline/HDShaderIDs.cs


internal static readonly int _Cubemap = Shader.PropertyToID("_Cubemap");
internal static readonly int _SkyParam = Shader.PropertyToID("_SkyParam");
internal static readonly int _PixelCoordToViewDirWS = Shader.PropertyToID("_PixelCoordToViewDirWS");
}
}

43
Assets/ScriptableRenderPipeline/HDRenderPipeline/Sky/GGXConvolve.shader


#include "../../ShaderLibrary/ImageBasedLighting.hlsl"
#include "SkyManager.cs.hlsl"
TEXTURECUBE(_MainTex);
SAMPLERCUBE(sampler_MainTex);
TEXTURE2D_FLOAT(_GgxIblSamples);
#ifdef USE_MIS
TEXTURE2D(_MarginalRowDensities);
TEXTURE2D(_ConditionalDensities);
#endif
float _Level;
float _LastLevel;
float _InvOmegaP;
float4x4 _PixelCoordToViewDirWS; // Actually just 3x3, but Unity can only set 4x4
float3 positionCS : POSITION;
float3 eyeVector : NORMAL;
uint vertexID : SV_VertexID;
float3 eyeVector : TEXCOORD0;
// Unity renders upside down, so the clip space coordinates have to be flipped.
output.positionCS = float4(input.positionCS.x, -input.positionCS.y, UNITY_RAW_FAR_CLIP_VALUE, 1.0);
output.eyeVector = input.eyeVector;
output.positionCS = GetFullScreenTriangleVertexPosition(input.vertexID);
TEXTURECUBE(_MainTex);
SAMPLERCUBE(sampler_MainTex);
TEXTURE2D_FLOAT(_GgxIblSamples);
#ifdef USE_MIS
TEXTURE2D(_MarginalRowDensities);
TEXTURE2D(_ConditionalDensities);
#endif
float _Level;
float _LastLevel;
float _InvOmegaP;
// Vector interpolation is not magnitude-preserving.
float3 N = normalize(input.eyeVector);
// Points towards the camera
float3 viewDirWS = normalize(mul(float3(input.positionCS.xy, 1.0), (float3x3)_PixelCoordToViewDirWS));
// Reverse it to point into the scene
float3 N = -viewDirWS;
// Remove view-dependency from GGX, effectively making the BSDF isotropic.
float3 V = N;

6
Assets/ScriptableRenderPipeline/HDRenderPipeline/Sky/HDRISky/HDRISkyRenderer.cs


m_SkyHDRIMaterial.SetTexture(HDShaderIDs._Cubemap, m_HdriSkyParams.skyHDRI);
m_SkyHDRIMaterial.SetVector(HDShaderIDs._SkyParam, new Vector4(m_HdriSkyParams.exposure, m_HdriSkyParams.multiplier, m_HdriSkyParams.rotation, 0.0f));
builtinParams.commandBuffer.DrawMesh(builtinParams.skyMesh, Matrix4x4.identity, m_SkyHDRIMaterial, 0, renderForCubemap ? 0 : 1);
// This matrix needs to be updated at the draw call frequency.
MaterialPropertyBlock properties = new MaterialPropertyBlock();
properties.SetMatrix(HDShaderIDs._PixelCoordToViewDirWS, builtinParams.pixelCoordToViewDirMatrix);
Utilities.DrawFullScreen(builtinParams.commandBuffer, m_SkyHDRIMaterial, properties, renderForCubemap ? 0 : 1);
}
public override bool IsSkyValid()

16
Assets/ScriptableRenderPipeline/HDRenderPipeline/Sky/HDRISky/Resources/SkyHDRI.shader


SAMPLERCUBE(sampler_Cubemap);
float4 _SkyParam; // x exposure, y multiplier, z rotation
float4x4 _PixelCoordToViewDirWS; // Actually just 3x3, but Unity can only set 4x4
float3 positionCS : POSITION;
float3 eyeVector : NORMAL;
uint vertexID : SV_VertexID;
float3 eyeVector : TEXCOORD0;
// TODO: implement SV_vertexID full screen quad
// Unity renders upside down, so the clip space coordinates have to be flipped.
output.positionCS = float4(input.positionCS.x, -input.positionCS.y, UNITY_RAW_FAR_CLIP_VALUE, 1.0);
output.eyeVector = input.eyeVector;
output.positionCS = GetFullScreenTriangleVertexPosition(input.vertexID, UNITY_RAW_FAR_CLIP_VALUE);
float3 dir = normalize(input.eyeVector);
// Points towards the camera
float3 viewDirWS = normalize(mul(float3(input.positionCS.xy, 1.0), (float3x3)_PixelCoordToViewDirWS));
// Reverse it to point into the scene
float3 dir = -viewDirWS;
// Rotate direction
float phi = DegToRad(_SkyParam.z);

4
Assets/ScriptableRenderPipeline/HDRenderPipeline/Sky/ProceduralSky/ProceduralSkyRenderer.cs


// We do not render the height fog into the sky IBL cubemap.
properties.SetFloat("_HeightRayleighDensity", renderForCubemap ? -0.0f : -param.heightRayleighDensity / 100000f);
properties.SetFloat("_HeightMieDensity", renderForCubemap ? -0.0f : -param.heightMieDensity / 100000f);
properties.SetMatrix(HDShaderIDs._PixelCoordToViewDirWS, builtinParams.pixelCoordToViewDirMatrix);
}
override public void RenderSky(BuiltinSkyParameters builtinParams, SkySettings skyParameters, bool renderForCubemap)

// Set shader constants.
SetUniforms(builtinParams, m_ProceduralSkySettings, renderForCubemap, ref properties);
builtinParams.commandBuffer.DrawMesh(builtinParams.skyMesh, Matrix4x4.identity, m_ProceduralSkyMaterial, 0, 0, properties);
Utilities.DrawFullScreen(builtinParams.commandBuffer, m_ProceduralSkyMaterial, properties);
}
}
}

17
Assets/ScriptableRenderPipeline/HDRenderPipeline/Sky/ProceduralSky/Resources/SkyProcedural.shader


// x exposure, y multiplier, z rotation
float4 _SkyParam;
float4x4 _PixelCoordToViewDirWS; // Actually just 3x3, but Unity can only set 4x4
// x = width, y = height, z = 1.0/width, w = 1.0/height
float4 _ScreenSize;

#define IS_RENDERING_SKY
#include "AtmosphericScattering.hlsl"
float3 positionCS : POSITION;
float3 eyeVector : NORMAL;
uint vertexID : SV_VertexID;
float3 eyeVector : TEXCOORD0;
// TODO: implement SV_vertexID full screen quad
// Unity renders upside down, so the clip space coordinates have to be flipped.
output.positionCS = float4(input.positionCS.x, -input.positionCS.y, UNITY_RAW_FAR_CLIP_VALUE, 1.0);
output.eyeVector = input.eyeVector;
output.positionCS = GetFullScreenTriangleVertexPosition(input.vertexID, UNITY_RAW_FAR_CLIP_VALUE);
float3 dir = normalize(input.eyeVector);
// Points towards the camera
float3 viewDirWS = normalize(mul(float3(input.positionCS.xy, 1.0), (float3x3)_PixelCoordToViewDirWS));
// Reverse it to point into the scene
float3 dir = -viewDirWS;
// Rotate direction
float phi = DegToRad(_SkyParam.z);

23
Assets/ScriptableRenderPipeline/HDRenderPipeline/Sky/RuntimeFilterIBL.cs


void FilterCubemapCommon(CommandBuffer cmd,
Texture source, RenderTexture target, int mipCount,
Mesh[] cubemapFaceMesh)
Matrix4x4[] worldToViewMatrices)
{
// Solid angle associated with a texel of the cubemap.
float invOmegaP = (6.0f * source.width * source.width) / (4.0f * Mathf.PI);

string sampleName = String.Format("Filter Cubemap Mip {0}", mip);
cmd.BeginSample(sampleName);
MaterialPropertyBlock props = new MaterialPropertyBlock();
props.SetFloat("_Level", mip);
Vector4 faceSize = new Vector4(source.width >> mip, source.height >> mip, 1.0f / (source.width >> mip), 1.0f / (source.height >> mip));
Matrix4x4 transform = SkyManager.ComputePixelCoordToWorldSpaceViewDirectionMatrix(0.5f * Mathf.PI, faceSize, worldToViewMatrices[face], true);
MaterialPropertyBlock props = new MaterialPropertyBlock();
props.SetFloat("_Level", mip);
props.SetMatrix(HDShaderIDs._PixelCoordToViewDirWS, transform);
cmd.DrawMesh(cubemapFaceMesh[face], Matrix4x4.identity, m_GgxConvolveMaterial, 0, 0, props);
Utilities.DrawFullScreen(cmd, m_GgxConvolveMaterial, props);
}
cmd.EndSample(sampleName);
}

public void FilterCubemap(CommandBuffer cmd,
Texture source, RenderTexture target, int mipCount,
Mesh[] cubemapFaceMesh)
Matrix4x4[] worldToViewMatrices)
FilterCubemapCommon(cmd, source, target, mipCount, cubemapFaceMesh);
FilterCubemapCommon(cmd, source, target, mipCount, worldToViewMatrices);
}
// Filters MIP map levels (other than 0) with GGX using multiple importance sampling.

Mesh[] cubemapFaceMesh)
Matrix4x4[] worldToViewMatrices)
{
// Bind the input cubemap.
m_BuildProbabilityTablesCS.SetTexture(m_ConditionalDensitiesKernel, "envMap", source);

m_GgxConvolveMaterial.SetTexture("_ConditionalDensities", conditionalCdf);
m_GgxConvolveMaterial.SetTexture("_MarginalRowDensities", marginalRowCdf);
FilterCubemapCommon(cmd, source, target, mipCount, cubemapFaceMesh);
FilterCubemapCommon(cmd, source, target, mipCount, worldToViewMatrices);
}
}
}

173
Assets/ScriptableRenderPipeline/HDRenderPipeline/Sky/SkyManager.cs


public class BuiltinSkyParameters
{
public Matrix4x4 pixelCoordToViewDirMatrix;
public Mesh skyMesh;
public CommandBuffer commandBuffer;
public Light sunLight;
public RenderTargetIdentifier colorBuffer;

IBLFilterGGX m_iblFilterGgx = null;
Vector4 m_CubemapScreenSize;
Matrix4x4[] m_faceCameraViewProjectionMatrix = new Matrix4x4[6];
Matrix4x4[] m_faceWorldToViewMatrixMatrices = new Matrix4x4[6];
Matrix4x4[] m_facePixelCoordToViewDirMatrices = new Matrix4x4[6];
Mesh[] m_CubemapFaceMesh = new Mesh[6];
BuiltinSkyParameters m_BuiltinParameters = new BuiltinSkyParameters();
SkyRenderer m_Renderer = null;

public Texture skyReflection { get { return m_SkyboxGGXCubemapRT; } }
protected Mesh BuildSkyMesh(Vector3 cameraPosition, Matrix4x4 cameraInvViewProjectionMatrix)
{
// These are clip space coords.
Vector4 vertData0 = new Vector4(-1.0f, -1.0f, 0.0f, 1.0f);
Vector4 vertData1 = new Vector4( 1.0f, -1.0f, 0.0f, 1.0f);
Vector4 vertData2 = new Vector4( 1.0f, 1.0f, 0.0f, 1.0f);
Vector4 vertData3 = new Vector4(-1.0f, 1.0f, 0.0f, 1.0f);
Vector3[] vertData = new Vector3[4];
vertData[0] = new Vector3(vertData0.x, vertData0.y, vertData0.z);
vertData[1] = new Vector3(vertData1.x, vertData1.y, vertData1.z);
vertData[2] = new Vector3(vertData2.x, vertData2.y, vertData2.z);
vertData[3] = new Vector3(vertData3.x, vertData3.y, vertData3.z);
// Get view vector based on the frustum, i.e (invert transform frustum get position etc...)
Vector3[] eyeVectorData = new Vector3[4];
Matrix4x4 transformMatrix = cameraInvViewProjectionMatrix;
Vector4 posWorldSpace0 = transformMatrix * vertData0;
Vector4 posWorldSpace1 = transformMatrix * vertData1;
Vector4 posWorldSpace2 = transformMatrix * vertData2;
Vector4 posWorldSpace3 = transformMatrix * vertData3;
Vector4 cameraPos = new Vector4(cameraPosition.x, cameraPosition.y, cameraPosition.z, 0.0f);
Vector4 direction0 = (posWorldSpace0 / posWorldSpace0.w - cameraPos);
Vector4 direction1 = (posWorldSpace1 / posWorldSpace1.w - cameraPos);
Vector4 direction2 = (posWorldSpace2 / posWorldSpace2.w - cameraPos);
Vector4 direction3 = (posWorldSpace3 / posWorldSpace3.w - cameraPos);
if (SystemInfo.graphicsUVStartsAtTop)
{
eyeVectorData[3] = new Vector3(direction0.x, direction0.y, direction0.z).normalized;
eyeVectorData[2] = new Vector3(direction1.x, direction1.y, direction1.z).normalized;
eyeVectorData[1] = new Vector3(direction2.x, direction2.y, direction2.z).normalized;
eyeVectorData[0] = new Vector3(direction3.x, direction3.y, direction3.z).normalized;
}
else
{
eyeVectorData[0] = new Vector3(direction0.x, direction0.y, direction0.z).normalized;
eyeVectorData[1] = new Vector3(direction1.x, direction1.y, direction1.z).normalized;
eyeVectorData[2] = new Vector3(direction2.x, direction2.y, direction2.z).normalized;
eyeVectorData[3] = new Vector3(direction3.x, direction3.y, direction3.z).normalized;
}
// Write out the mesh
var triangles = new int[6] { 0, 1, 2, 2, 3, 0 };
return new Mesh
{
vertices = vertData,
normals = eyeVectorData,
triangles = triangles
};
}
void RebuildTextures(SkySettings skySettings)
{
int resolution = 256;

m_CubemapScreenSize = new Vector4((float)resolution, (float)resolution, 1.0f / (float)resolution, 1.0f / (float)resolution);
}
void RebuildSkyMeshes(float nearPlane, float farPlane)
void RebuildSkyMatrices(float nearPlane, float farPlane)
if (m_CubemapFaceMesh[0] == null)
if (!m_SkySettings) return;
Matrix4x4 cubeProj = Matrix4x4.Perspective(90.0f, 1.0f, nearPlane, farPlane);
// Ref: https://msdn.microsoft.com/en-us/library/windows/desktop/bb204881(v=vs.85).aspx
Vector3[] lookAtList =
Matrix4x4 cubeProj = Matrix4x4.Perspective(90.0f, 1.0f, nearPlane, farPlane);
new Vector3(1.0f, 0.0f, 0.0f),
new Vector3(-1.0f, 0.0f, 0.0f),
new Vector3(0.0f, 1.0f, 0.0f),
new Vector3(0.0f, -1.0f, 0.0f),
new Vector3(0.0f, 0.0f, 1.0f),
new Vector3(0.0f, 0.0f, -1.0f),
};
Vector3[] upVectorList =
{
new Vector3(0.0f, 1.0f, 0.0f),
new Vector3(0.0f, 1.0f, 0.0f),
new Vector3(0.0f, 0.0f, -1.0f),
new Vector3(0.0f, 0.0f, 1.0f),
new Vector3(0.0f, 1.0f, 0.0f),
new Vector3(0.0f, 1.0f, 0.0f),
};
Vector3[] lookAtList =
{
new Vector3(1.0f, 0.0f, 0.0f),
new Vector3(-1.0f, 0.0f, 0.0f),
new Vector3(0.0f, 1.0f, 0.0f),
new Vector3(0.0f, -1.0f, 0.0f),
new Vector3(0.0f, 0.0f, 1.0f),
new Vector3(0.0f, 0.0f, -1.0f),
};
for (int i = 0; i < 6; ++i)
{
Matrix4x4 lookAt = Matrix4x4.LookAt(Vector3.zero, lookAtList[i], upVectorList[i]);
Matrix4x4 worldToView = lookAt * Matrix4x4.Scale(new Vector3(1.0f, 1.0f, -1.0f)); // Need to scale -1.0 on Z to match what is being done in the camera.wolrdToCameraMatrix API. ...
Vector4 screenSize = new Vector4((int)m_SkySettings.resolution, (int)m_SkySettings.resolution, 1.0f / (int)m_SkySettings.resolution, 1.0f / (int)m_SkySettings.resolution);
Vector3[] UpVectorList =
{
new Vector3(0.0f, 1.0f, 0.0f),
new Vector3(0.0f, 1.0f, 0.0f),
new Vector3(0.0f, 0.0f, -1.0f),
new Vector3(0.0f, 0.0f, 1.0f),
new Vector3(0.0f, 1.0f, 0.0f),
new Vector3(0.0f, 1.0f, 0.0f),
};
m_faceWorldToViewMatrixMatrices[i] = worldToView;
m_facePixelCoordToViewDirMatrices[i] = ComputePixelCoordToWorldSpaceViewDirectionMatrix(0.5f * Mathf.PI, screenSize, worldToView, true);
m_faceCameraInvViewProjectionMatrix[i] = Utilities.GetViewProjectionMatrix(lookAt, cubeProj).inverse;
}
}
for (int i = 0; i < 6; ++i)
{
Matrix4x4 lookAt = Matrix4x4.LookAt(Vector3.zero, lookAtList[i], UpVectorList[i]);
m_faceCameraViewProjectionMatrix[i] = Utilities.GetViewProjectionMatrix(lookAt, cubeProj);
m_faceCameraInvViewProjectionMatrix[i] = m_faceCameraViewProjectionMatrix[i].inverse;
public static Matrix4x4 ComputePixelCoordToWorldSpaceViewDirectionMatrix(float verticalFoV, Vector4 screenSize, Matrix4x4 worldToViewMatrix, bool renderToCubemap)
{
// Compose the view space version first.
// V = -(X, Y, Z), s.t. Z = 1,
// X = (2x / resX - 1) * tan(vFoV / 2) * ar = x * [(2 / resX) * tan(vFoV / 2) * ar] + [-tan(vFoV / 2) * ar] = x * [-m00] + [-m20]
// Y = (2y / resY - 1) * tan(vFoV / 2) = y * [(2 / resY) * tan(vFoV / 2)] + [-tan(vFoV / 2)] = y * [-m11] + [-m21]
float tanHalfVertFoV = Mathf.Tan(0.5f * verticalFoV);
float aspectRatio = screenSize.x * screenSize.w;
m_CubemapFaceMesh[i] = BuildSkyMesh(Vector3.zero, m_faceCameraInvViewProjectionMatrix[i]);
}
// Compose the matrix.
float m21 = tanHalfVertFoV;
float m20 = tanHalfVertFoV * aspectRatio;
float m00 = -2.0f * screenSize.z * m20;
float m11 = -2.0f * screenSize.w * m21;
float m33 = -1.0f;
if (renderToCubemap)
{
// Flip Y.
m11 = -m11;
m21 = -m21;
Matrix4x4 viewSpaceRasterTransform = new Matrix4x4(new Vector4( m00, 0.0f, 0.0f, 0.0f),
new Vector4(0.0f, m11, 0.0f, 0.0f),
new Vector4( m20, m21, m33, 0.0f),
new Vector4(0.0f, 0.0f, 0.0f, 1.0f));
// Remove the translation component.
Vector4 homogeneousZero = new Vector4(0, 0, 0, 1);
worldToViewMatrix.SetColumn(3, homogeneousZero);
// Flip the Z to make the coordinate system left-handed.
worldToViewMatrix.SetRow(2, -worldToViewMatrix.GetRow(2));
// Transpose for HLSL.
return Matrix4x4.Transpose(worldToViewMatrix.transpose * viewSpaceRasterTransform);
}
// Sets the global MIP-mapped cubemap '_SkyTexture' in the shader.

{
// When loading RenderDoc, RenderTextures will go null
RebuildTextures(skySettings);
RebuildSkyMeshes(nearPlane, farPlane);
RebuildSkyMatrices(nearPlane, farPlane);
}
public void Build(RenderPipelineResources renderPipelinesResources)

{
for (int i = 0; i < 6; ++i)
{
builtinParams.pixelCoordToViewDirMatrix = m_facePixelCoordToViewDirMatrices[i];
builtinParams.screenSize = m_CubemapScreenSize;
builtinParams.skyMesh = m_CubemapFaceMesh[i];
builtinParams.colorBuffer = target;
builtinParams.depthBuffer = BuiltinSkyParameters.nullRT;

{
if (m_useMIS && m_iblFilterGgx.SupportMIS)
{
m_iblFilterGgx.FilterCubemapMIS(cmd, input, target, mipCount, m_SkyboxConditionalCdfRT, m_SkyboxMarginalRowCdfRT, m_CubemapFaceMesh);
m_iblFilterGgx.FilterCubemapMIS(cmd, input, target, mipCount, m_SkyboxConditionalCdfRT, m_SkyboxMarginalRowCdfRT, m_faceWorldToViewMatrixMatrices);
m_iblFilterGgx.FilterCubemap(cmd, input, target, mipCount, m_CubemapFaceMesh);
m_iblFilterGgx.FilterCubemap(cmd, input, target, mipCount, m_faceWorldToViewMatrixMatrices);
}
}
}

m_BuiltinParameters.commandBuffer = cmd;
m_BuiltinParameters.sunLight = sunLight;
m_BuiltinParameters.screenSize = m_CubemapScreenSize;
m_BuiltinParameters.cameraPosWS = camera.camera.transform.position;
if (
m_UpdatedFramesRequired > 0 ||

{
m_BuiltinParameters.commandBuffer = cmd;
m_BuiltinParameters.sunLight = sunLight;
m_BuiltinParameters.pixelCoordToViewDirMatrix = ComputePixelCoordToWorldSpaceViewDirectionMatrix(camera.camera.fieldOfView * Mathf.Deg2Rad, camera.screenSize, camera.viewMatrix, false);
m_BuiltinParameters.screenSize = camera.screenSize;
m_BuiltinParameters.skyMesh = BuildSkyMesh(camera.camera.GetComponent<Transform>().position, m_BuiltinParameters.invViewProjMatrix);
m_BuiltinParameters.colorBuffer = colorBuffer;
m_BuiltinParameters.depthBuffer = depthBuffer;

7
Assets/ScriptableRenderPipeline/HDRenderPipeline/Utilities.cs


// Draws a full screen triangle as a faster alternative to drawing a full screen quad.
public static void DrawFullScreen(CommandBuffer commandBuffer, Material material,
MaterialPropertyBlock properties = null, int shaderPassID = 0)
{
commandBuffer.DrawProcedural(Matrix4x4.identity, material, shaderPassID, MeshTopology.Triangles, 3, 1, properties);
}
// Draws a full screen triangle as a faster alternative to drawing a full screen quad.
public static void DrawFullScreen(CommandBuffer commandBuffer, Material material,
RenderTargetIdentifier colorBuffer,
MaterialPropertyBlock properties = null, int shaderPassID = 0)
{

4
Assets/ScriptableRenderPipeline/ShaderLibrary/Common.hlsl


#endif
}
float4 GetFullScreenTriangleVertexPosition(uint vertexID)
float4 GetFullScreenTriangleVertexPosition(uint vertexID, float z = UNITY_NEAR_CLIP_VALUE)
return float4(uv * 2.0 - 1.0, 1.0, 1.0);
return float4(uv * 2.0 - 1.0, z, 1.0);
}
// LOD dithering transition helper

正在加载...
取消
保存