浏览代码

cleanup and refactor shader code into common functions

/main
Filip Iliescu 7 年前
当前提交
01556aaf
共有 4 个文件被更改,包括 121 次插入304 次删除
  1. 100
      Assets/ScriptableRenderPipeline/MobileRenderPipeline/ClassicDeferred/ClassicDeferredPipeline.cs
  2. 116
      Assets/ScriptableRenderPipeline/MobileRenderPipeline/ClassicDeferred/Internal-DeferredReflections.shader
  3. 134
      Assets/ScriptableRenderPipeline/MobileRenderPipeline/ClassicDeferred/Internal-DeferredShading.shader
  4. 75
      Assets/ScriptableRenderPipeline/MobileRenderPipeline/ClassicDeferred/LightingTemplate.hlsl

100
Assets/ScriptableRenderPipeline/MobileRenderPipeline/ClassicDeferred/ClassicDeferredPipeline.cs


public Mesh m_QuadMesh;
public Mesh m_BoxMesh;
public Texture m_DefaultSpotCookie;
public Shader finalPassShader;
public Shader deferredShader;
public Shader deferredReflectionShader;

private static int s_GBufferNormal;
private static int s_GBufferEmission;
private static int s_GBufferRedF32;
//private static int s_CameraReflectionsTexture;
private static int m_quadLightingPassNdx;
private static int m_FiniteLightingPassNdx;
private Material m_DeferredMaterial;
private Material m_DirectionalDeferredLightingMaterial;
private Material m_FiniteDeferredLightingMaterial;
public Texture m_DefaultSpotCookie;
private void OnValidate()
{
Build();

{
if (m_BlitMaterial) DestroyImmediate(m_BlitMaterial);
if (m_DeferredMaterial) DestroyImmediate(m_DeferredMaterial);
if (m_DirectionalDeferredLightingMaterial) DestroyImmediate(m_DirectionalDeferredLightingMaterial);
if (m_FiniteDeferredLightingMaterial) DestroyImmediate(m_FiniteDeferredLightingMaterial);
if (m_ReflectionMaterial) DestroyImmediate (m_ReflectionMaterial);
if (m_ReflectionNearClipMaterial) DestroyImmediate (m_ReflectionNearClipMaterial);
if (m_ReflectionNearAndFarClipMaterial) DestroyImmediate (m_ReflectionNearAndFarClipMaterial);

m_BlitMaterial = new Material (finalPassShader) { hideFlags = HideFlags.HideAndDontSave };
m_DeferredMaterial = new Material (deferredShader) { hideFlags = HideFlags.HideAndDontSave };
m_quadLightingPassNdx = m_DeferredMaterial.FindPass ("DIRECTIONALLIGHT");
m_FiniteLightingPassNdx = m_DeferredMaterial.FindPass ("FINITELIGHT");
m_DirectionalDeferredLightingMaterial = new Material (deferredShader) { hideFlags = HideFlags.HideAndDontSave };
m_DirectionalDeferredLightingMaterial.SetInt("_SrcBlend", (int)BlendMode.One);
m_DirectionalDeferredLightingMaterial.SetInt("_DstBlend", (int)BlendMode.One);
m_DirectionalDeferredLightingMaterial.SetInt("_SrcABlend", (int)BlendMode.One);
m_DirectionalDeferredLightingMaterial.SetInt("_DstABlend", (int)BlendMode.Zero);
m_DirectionalDeferredLightingMaterial.SetInt("_CullMode", (int)CullMode.Off);
m_DirectionalDeferredLightingMaterial.SetInt("_CompareFunc", (int)CompareFunction.Always);
m_FiniteDeferredLightingMaterial = new Material (deferredShader) { hideFlags = HideFlags.HideAndDontSave };
m_FiniteDeferredLightingMaterial.SetInt("_SrcBlend", (int)BlendMode.One);
m_FiniteDeferredLightingMaterial.SetInt("_DstBlend", (int)BlendMode.One);
m_FiniteDeferredLightingMaterial.SetInt("_SrcABlend", (int)BlendMode.One);
m_FiniteDeferredLightingMaterial.SetInt("_DstABlend", (int)BlendMode.Zero);
m_FiniteDeferredLightingMaterial.SetInt("_CullMode", (int)CullMode.Back);
m_FiniteDeferredLightingMaterial.SetInt("_CompareFunc", (int)CompareFunction.LessEqual);
m_ReflectionMaterial = new Material (deferredReflectionShader) { hideFlags = HideFlags.HideAndDontSave };
m_ReflectionMaterial.SetInt("_SrcBlend", (int)BlendMode.DstAlpha);

m_ReflectionNearAndFarClipMaterial.SetInt("_DstABlend", (int)BlendMode.Zero);
m_ReflectionNearAndFarClipMaterial.SetInt("_CullMode", (int)CullMode.Off);
m_ReflectionNearAndFarClipMaterial.SetInt("_CompareFunc", (int)CompareFunction.Always);
//s_CameraReflectionsTexture = Shader.PropertyToID ("_CameraReflectionsTexture");
//shadows
m_MatWorldToShadow = new Matrix4x4[k_MaxLights * k_MaxShadowmapPerLights];
m_DirShadowSplitSpheres = new Vector4[k_MaxDirectionalSplit];

var boxProj = (rl.boxProjection != 0);
var probePosition = mat.GetColumn (3); // translation vector
var probePosition1 = new Vector4 (probePosition [0], probePosition [1], probePosition [2], boxProj ? 1f : 0f);
// C is reflection volume center in world space (NOT same as cube map capture point)

cmd.DrawMesh (m_QuadMesh, Matrix4x4.identity, m_ReflectionNearAndFarClipMaterial, 0, 0, props);
}
}
// void RenderApplyReflections(Camera camera, CommandBuffer cmd, CullResults cullResults, ScriptableRenderContext loop)
// {
// // draw offscreen accumulation buffer onto emission buffer
// var props = new MaterialPropertyBlock ();
//// props.SetFloat ("_LightAsQuad", 1);
// cmd.SetGlobalTexture ("_CameraReflectionsTexture", s_CameraReflectionsTexture);
// cmd.DrawMesh (m_QuadMesh, Matrix4x4.identity, m_DeferredReflectionMaterial, 0, m_ReflectionsApplyPassNdx, props);
// }
void RenderShadowMaps(CullResults cullResults, ScriptableRenderContext loop)
{

void RenderLighting (Camera camera, CullResults inputs, ScriptableRenderContext loop)
{
// {
// var cmd = new CommandBuffer { name = "Reflections" };
//
// // setup offscreen render target for reflections
// cmd.GetTemporaryRT (s_CameraReflectionsTexture, camera.pixelWidth, camera.pixelHeight, 0, FilterMode.Point, RenderTextureFormat.DefaultHDR, RenderTextureReadWrite.Linear);
// cmd.SetRenderTarget (new RenderTargetIdentifier (s_CameraReflectionsTexture), new RenderTargetIdentifier (s_GBufferZ));
//
// cmd.ClearRenderTarget (false, true, new Color(0, 0, 0, 1));
//
// RenderReflections (camera, cmd, inputs, loop);
//
// loop.ExecuteCommandBuffer (cmd);
// cmd.Dispose ();
// }
var cmd = new CommandBuffer { name = "Lighting" };
{
var cmd = new CommandBuffer { name = "Lighting" };
// IF PLATFORM_MAC -- cannot use framebuffer fetch
#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
cmd.SetRenderTarget (new RenderTargetIdentifier (s_GBufferEmission), new RenderTargetIdentifier (s_GBufferZ));
#endif
// IF PLATFORM_MAC -- cannot use framebuffer fetch
#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
cmd.SetRenderTarget (new RenderTargetIdentifier (s_GBufferEmission), new RenderTargetIdentifier (s_GBufferZ));
#endif
foreach (var light in inputs.visibleLights) {
RenderLightGeometry (camera, light, cmd, loop);
}
foreach (var light in inputs.visibleLights) {
RenderLightGeometry (camera, light, cmd, loop);
}
// TODO: UNITY_BRDF_PBS1 writes out alpha 1 to our emission alpha. Should preclear emission alpha after gbuffer pass in case this ever changes
RenderReflections (camera, cmd, inputs, loop);
// TODO: UNITY_BRDF_PBS1 writes out alpha 1 to our emission alpha. Should preclear emission alpha after gbuffer pass in case this ever changes
RenderReflections (camera, cmd, inputs, loop);
loop.ExecuteCommandBuffer (cmd);
cmd.Dispose ();
}
loop.ExecuteCommandBuffer (cmd);
cmd.Dispose ();
}
void RenderLightGeometry (Camera camera, VisibleLight light, CommandBuffer cmd, ScriptableRenderContext loop)

cmd.EnableShaderKeyword ("POINT_COOKIE");
if (renderAsQuad) {
cmd.DrawMesh (m_QuadMesh, Matrix4x4.identity, m_DeferredMaterial, 0, m_quadLightingPassNdx, props);
cmd.DrawMesh (m_QuadMesh, Matrix4x4.identity, m_DirectionalDeferredLightingMaterial, 0, 0, props);
cmd.DrawMesh (m_PointLightMesh, matrix, m_DeferredMaterial, 0, m_FiniteLightingPassNdx, props);
cmd.DrawMesh (m_PointLightMesh, matrix, m_FiniteDeferredLightingMaterial, 0, 0, props);
}
} else if ((light.lightType == LightType.Spot)) {

cmd.SetGlobalTexture ("_LightTexture0", m_DefaultSpotCookie);
if (renderAsQuad) {
cmd.DrawMesh (m_QuadMesh, Matrix4x4.identity, m_DeferredMaterial, 0, m_quadLightingPassNdx, props);
cmd.DrawMesh (m_QuadMesh, Matrix4x4.identity, m_DirectionalDeferredLightingMaterial, 0, 0, props);
cmd.DrawMesh (m_SpotLightMesh, lightToWorld, m_DeferredMaterial, 0, m_FiniteLightingPassNdx, props);
cmd.DrawMesh (m_SpotLightMesh, lightToWorld, m_FiniteDeferredLightingMaterial, 0, 0, props);
}
} else {

if (cookie != null)
cmd.EnableShaderKeyword ("DIRECTIONAL_COOKIE");
cmd.DrawMesh (m_QuadMesh, Matrix4x4.identity, m_DeferredMaterial, 0, m_quadLightingPassNdx, props);
cmd.DrawMesh (m_QuadMesh, Matrix4x4.identity, m_DirectionalDeferredLightingMaterial, 0, 0, props);
}
}

116
Assets/ScriptableRenderPipeline/MobileRenderPipeline/ClassicDeferred/Internal-DeferredReflections.shader


SubShader {
// Calculates reflection contribution from a single probe (rendered as cubes) or default reflection (rendered as full screen quad)
// Finite: Blend DstAlpha One, DstAlpha Zero
// clipping near plane: Cull Front; ZTest GEqual; Blend DstAlpha One, DstAlpha Zero
// renderAsQuad: Cull Off; ZTest Always; Blend DstAlpha One, DstAlpha Zero
//Blend DstAlpha One, DstAlpha Zero
// clipping near plane
// ZWrite Off
// Cull Front
// ZTest GEqual
// Blend DstAlpha One, DstAlpha Zero
// renderAsQuad
// ZWrite Off
// Cull Off
// ZTest Always
// Blend DstAlpha One, DstAlpha Zero
#pragma vertex filip_vert_deferred
#pragma vertex onchip_vert_deferred
#pragma fragment frag
#include "UnityCG.cginc"

#include "UnityStandardBRDF.cginc"
#include "UnityPBSLighting.cginc"
#ifndef UNITY_FRAMEBUFFER_FETCH_AVAILABLE
sampler2D _CameraGBufferTexture0;
sampler2D _CameraGBufferTexture1;
sampler2D _CameraGBufferTexture2;
#endif
unity_v2f_deferred filip_vert_deferred (float4 vertex : POSITION, float3 normal : NORMAL)
{
bool lightAsQuad = _LightAsQuad!=0.0;
unity_v2f_deferred o;
// scaling quasd by two becuase built-in unity quad ranges from -0.5 to 0.5
o.pos = lightAsQuad ? float4(2.0*vertex.xy, 0.5, 1.0) : UnityObjectToClipPos(vertex);
o.uv = ComputeScreenPos(o.pos);
// normal contains a ray pointing from the camera to one of near plane's
// corners in camera space when we are drawing a full screen quad.
// Otherwise, when rendering 3D shapes, use the ray calculated here.
if (lightAsQuad){
float2 rayXY = mul(unity_CameraInvProjection, float4(o.pos.x, -o.pos.y, -1, 1)).xy;
o.ray = float3(rayXY, 1.0);
}
else
{
o.ray = UnityObjectToViewPos(vertex) * float3(-1,-1,1);
}
return o;
}
#include "LightingTemplate.hlsl"
half3 distanceFromAABB(half3 p, half3 aabbMin, half3 aabbMax)
{

half4 frag (unity_v2f_deferred i) : SV_TARGET
#endif
{
//return half4(1.0, 0.0, 0.0, 1.0);
float2 uv;
float4 viewPos;
float3 worldPos;
// Stripped from UnityDeferredCalculateLightParams, refactor into function ?
i.ray = i.ray * (_ProjectionParams.z / i.ray.z);
float2 uv = i.uv.xy / i.uv.w;
// read depth and reconstruct world position
float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, uv);
OnChipDeferredFragSetup(i, uv, viewPos, worldPos, 0.0);
float depth = outLinearDepth;
OnChipDeferredFragSetup(i, uv, viewPos, worldPos, outLinearDepth);
depth = Linear01Depth (depth);
float4 viewPos = float4(i.ray * depth,1);
float3 worldPos = mul (unity_CameraToWorld, viewPos).xyz;
#ifndef UNITY_FRAMEBUFFER_FETCH_AVAILABLE
// unpack Gbuffer

light.color = half3(0, 0, 0);
light.dir = half3(0, 1, 0);
// ---
// ---
// Calculate falloff value, so reflections on the edges of the probe would gradually blend to previous reflection.
// Also this ensures that pixels not located in the reflection probe AABB won't

ENDCG
}
//// Adds reflection buffer to the lighting buffer
//Pass
//{
// Name "DEFERRED_APPLY_REFLECTIONS"
//
// ZWrite Off
// ZTest Always
// Blend One One
// Cull Off
//
// CGPROGRAM
// #pragma target 4.5
// #pragma vertex refl_apply_vert_deferred
// #pragma fragment frag
// #pragma multi_compile ___ UNITY_HDR_ON
//
// #include "UnityCG.cginc"
//
// sampler2D _CameraReflectionsTexture;
//
// struct v2f {
// float2 uv : TEXCOORD0;
// float4 pos : SV_POSITION;
// };
//
// v2f refl_apply_vert_deferred (float4 vertex : POSITION, float3 normal : NORMAL)
// {
// // scaling quasd by two becuase built-in unity quad ranges from -0.5 to 0.5
// v2f o;
// o.pos = float4(2.0*vertex.xy, 0.5, 1.0);
// o.uv = ComputeScreenPos(o.pos);
//
// return o;
// }
//
// half4 frag (v2f i) : SV_Target
// {
// half4 c = tex2D (_CameraReflectionsTexture, i.uv);
// #ifdef UNITY_HDR_ON
// return float4(c.rgb, 0.0f);
// #else
// return float4(exp2(-c.rgb), 0.0f);
// #endif
//
// }
// ENDCG
//}
}
Fallback Off

134
Assets/ScriptableRenderPipeline/MobileRenderPipeline/ClassicDeferred/Internal-DeferredShading.shader


_LightTexture0 ("", any) = "" {}
_LightTextureB0 ("", 2D) = "" {}
_ShadowMapTexture ("", any) = "" {}
}
SubShader {
// Pass 1: Finite Lighting pass -- cuz thats how i roll
// LDR case - Lighting encoded into a subtractive ARGB8 buffer
// HDR case - Lighting additively blended into floating point buffer
Pass {
Name "FINITELIGHT"
ZWrite Off
Blend One One, One Zero
CGPROGRAM
#pragma target 3.0
#pragma vertex vert_deferred
#pragma fragment frag
#pragma multi_compile_lightpass
#pragma multi_compile ___ UNITY_HDR_ON
_SrcBlend ("", Float) = 1
_DstBlend ("", Float) = 1
_SrcABlend ("", Float) = 1
_DstABlend ("", Float) = 1
#pragma exclude_renderers nomrt
_CullMode ("", Float) = 0
_CompareFunc ("", Float) = 0
#include "UnityCG.cginc"
#include "UnityDeferredLibrary.cginc"
#include "UnityPBSLighting.cginc"
#include "UnityStandardUtils.cginc"
#include "UnityGBuffer.cginc"
#include "UnityStandardBRDF.cginc"
#include "LightingTemplate.hlsl"
#ifdef UNITY_FRAMEBUFFER_FETCH_AVAILABLE
void frag (unity_v2f_deferred i,
in half4 outGBuffer0 : SV_Target0,
in half4 outGBuffer1 : SV_Target1,
in half4 outGBuffer2 : SV_Target2,
out half4 outEmission : SV_Target3,
in float outLinearDepth : SV_Target4)
#else
half4 frag (unity_v2f_deferred i) : SV_TARGET
#endif
{
#ifdef UNITY_FRAMEBUFFER_FETCH_AVAILABLE
outEmission = CalculateLight(i, outGBuffer0, outGBuffer1, outGBuffer2, outLinearDepth);
#else
return CalculateLight(i);
#endif
SubShader {
ENDCG
}
// Pass 1.5: Directional Lighting pass -- becuase i said so
Name "DIRECTIONALLIGHT"
ZTest Always
Cull Off
Blend One One, One Zero
ZTest [_CompareFunc]
Cull [_CullMode]
Blend [_SrcBlend] [_DstBlend], [_SrcABlend] [_DstABlend]
#pragma target 3.0
#pragma vertex filip_vert_deferred
#pragma target 4.5
#pragma vertex onchip_vert_deferred
#pragma fragment frag
#pragma multi_compile_lightpass
#pragma multi_compile ___ UNITY_HDR_ON

#include "LightingTemplate.hlsl"
unity_v2f_deferred filip_vert_deferred (float4 vertex : POSITION, float3 normal : NORMAL)
{
bool lightAsQuad = _LightAsQuad!=0.0;
unity_v2f_deferred o;
// scaling quasd by two becuase built-in unity quad ranges from -0.5 to 0.5
o.pos = lightAsQuad ? float4(2.0*vertex.xy, 0.5, 1.0) : UnityObjectToClipPos(vertex);
o.uv = ComputeScreenPos(o.pos);
// normal contains a ray pointing from the camera to one of near plane's
// corners in camera space when we are drawing a full screen quad.
// Otherwise, when rendering 3D shapes, use the ray calculated here.
if (lightAsQuad){
float2 rayXY = mul(unity_CameraInvProjection, float4(o.pos.x, -o.pos.y, -1, 1)).xy;
o.ray = float3(rayXY, 1.0);
}
else
{
o.ray = UnityObjectToViewPos(vertex) * float3(-1,-1,1);
}
return o;
}
#ifdef UNITY_FRAMEBUFFER_FETCH_AVAILABLE
void frag (unity_v2f_deferred i,
in half4 outGBuffer0 : SV_Target0,

}
ENDCG
}
// Pass 2: Final decode pass.
// Used only with HDR off, to decode the logarithmic buffer into the main RT
Pass {
ZTest Always Cull Off ZWrite Off
Stencil {
ref [_StencilNonBackground]
readmask [_StencilNonBackground]
// Normally just comp would be sufficient, but there's a bug and only front face stencil state is set (case 583207)
compback equal
compfront equal
}
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#pragma exclude_renderers nomrt
#include "UnityCG.cginc"
sampler2D _LightBuffer;
struct v2f {
float4 vertex : SV_POSITION;
float2 texcoord : TEXCOORD0;
};
v2f vert (float4 vertex : POSITION, float2 texcoord : TEXCOORD0)
{
v2f o;
o.vertex = UnityObjectToClipPos(vertex);
o.texcoord = texcoord.xy;
#ifdef UNITY_SINGLE_PASS_STEREO
o.texcoord = TransformStereoScreenSpaceTex(o.texcoord, 1.0f);
#endif
return o;
}
fixed4 frag (v2f i) : SV_Target
{
return -log2(tex2D(_LightBuffer, i.texcoord));
}
ENDCG
}
}

75
Assets/ScriptableRenderPipeline/MobileRenderPipeline/ClassicDeferred/LightingTemplate.hlsl


// --------------------------------------------------------
// Common lighting data calculation (direction, attenuation, ...)
void MyDeferredCalculateLightParams (
unity_v2f_deferred i,
out float3 outWorldPos,
void OnChipDeferredFragSetup (
inout unity_v2f_deferred i,
out half3 outLightDir,
out float outAtten,
out float outFadeDist,
out float4 outVPos,
out float3 outWPos,
float depth
)
{

// read depth and reconstruct world position
// if we have framebuffer fetch, its expected depth was passed in the parameter from the framebuffer so no need to fetch
#else
// depth = SAMPLE_DEPTH_TEXTURE(_CameraGBufferZ, uv);
outUV = uv;
outVPos = vpos;
outWPos = wpos;
}
void OnChipDeferredCalculateLightParams (
unity_v2f_deferred i,
out float3 outWorldPos,
out float2 outUV,
out half3 outLightDir,
out float outAtten,
out float outFadeDist,
float depth
)
{
float4 vpos;
float3 wpos;
float2 uv;
OnChipDeferredFragSetup(i, uv, vpos, wpos, depth);
// needed? old shadow code is commented out, we switched to new shadow code .. aka sampleShadow()
float fadeDist = UnityComputeShadowFadeDistance(wpos, vpos.z);
// spot light case

float att = dot(tolight, tolight) * _LightPos.w;
atten *= tex2D (_LightTextureB0, att.rr).UNITY_ATTEN_CHANNEL;
//atten *= UnityDeferredComputeShadow (wpos, fadeDist, uv);
//atten *= UnityDeferredComputeShadow (wpos, fadeDist, uv);
// directional light case
#elif defined (DIRECTIONAL) || defined (DIRECTIONAL_COOKIE)

//half UnityDeferredComputeShadow(float3 vec, float fadeDist, float2 uv)
//SampleShadow(uint type, float3 vPositionWs, float3 vPositionToLightDirWs, uint lightIndex)
atten *= SampleShadow(DIRECTIONAL_LIGHT, wpos, 0, 0);
#if defined (DIRECTIONAL_COOKIE)

float atten = tex2D (_LightTextureB0, att.rr).UNITY_ATTEN_CHANNEL;
// atten *= UnityDeferredComputeShadow (tolight, fadeDist, uv);
// atten *= SampleShadow(POINT_LIGHT, wpos, 0, 0);
#if defined (POINT_COOKIE)
atten *= texCUBEbias(_LightTexture0, float4(mul(unity_WorldToLight, half4(wpos,1)).xyz, -8)).w;
#endif //POINT_COOKIE

UNITY_INITIALIZE_OUTPUT(UnityLight, light);
#ifdef UNITY_FRAMEBUFFER_FETCH_AVAILABLE
MyDeferredCalculateLightParams (i, wpos, uv, light.dir, atten, fadeDist, vpDepth);
OnChipDeferredCalculateLightParams (i, wpos, uv, light.dir, atten, fadeDist, vpDepth);
MyDeferredCalculateLightParams (i, wpos, uv, light.dir, atten, fadeDist, 0.0);
OnChipDeferredCalculateLightParams (i, wpos, uv, light.dir, atten, fadeDist, 0.0);
#endif
light.color = _LightColor.rgb * atten;

ind.specular = 0;
// UNITY_BRDF_PBS1 writes out alpha 1 to our emission alpha.
unity_v2f_deferred onchip_vert_deferred (float4 vertex : POSITION, float3 normal : NORMAL)
{
bool lightAsQuad = _LightAsQuad!=0.0;
unity_v2f_deferred o;
// scaling quad by two becuase built-in unity quad ranges from -0.5 to 0.5
o.pos = lightAsQuad ? float4(2.0*vertex.xy, 0.5, 1.0) : UnityObjectToClipPos(vertex);
o.uv = ComputeScreenPos(o.pos);
// normal contains a ray pointing from the camera to one of near plane's
// corners in camera space when we are drawing a full screen quad.
// Otherwise, when rendering 3D shapes, use the ray calculated here.
if (lightAsQuad){
float2 rayXY = mul(unity_CameraInvProjection, float4(o.pos.x, -o.pos.y, -1, 1)).xy;
o.ray = float3(rayXY, 1.0);
}
else
{
o.ray = UnityObjectToViewPos(vertex) * float3(-1,-1,1);
}
return o;
}
#endif
正在加载...
取消
保存