Unity URP 平面反射笔记

这篇具有很好参考价值的文章主要介绍了Unity URP 平面反射笔记。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

urp镜面,Unity学习笔记,unity,平面,游戏引擎,Powered by 金山文档

平面反射的具体内容可以参考以下几篇文章,都总结得很不错:

  1. https://zhuanlan.zhihu.com/p/493766119?utm_id=0

  1. https://blog.csdn.net/Phantom1516/article/details/128762625

  1. https://blog.csdn.net/puppet_master/article/details/80808486

下面是平面反射实现的具体思路:

首先在原相机的基础上拷贝一个反射摄像机,其位置等的参数保持和原来的摄像机一样,之后我们会为这一个反射摄像机添加一个脚本,脚本会为这两个摄像机进行同步,并利用反射摄像机渲染出一张物体经过镜面反射的渲染纹理,最后将这一张渲染纹理传入到平面plane的Shader中进行采样,就可以实现反射效果。

对于世界空间中某一物体变换,可以概括为:先进行M操作将物体变换到世界空间,再利用反射矩阵将处于世界空间中的物体经过一次变换到以某一平面的镜面对称的位置,最后再进行VP操作变换到反射摄像机空间中并进行透视投影;

反射矩阵

首先是反射矩阵的构建,可以看下面这一篇文章,写的很详细这里不多添加:

https://blog.csdn.net/Phantom1516/article/details/128762625

反射矩阵的作用就是将物体在世界空间中变换到以某一平面为准的镜面位置,记住此次操作要在MVP操作的M之后,VP之前!;

下面是挂在反射摄像机的脚本,此脚本会输出物体经过反射矩阵变换,以及反射摄像机空间变换后和透视投影后的一张渲染纹理:

//用于挂在获取反射贴图的摄像机上;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Numerics;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using UnityEngine.SceneManagement;
using UnityEngine.UIElements;
using UnityEditor;
using Matrix4x4 = UnityEngine.Matrix4x4;
using Vector3 = UnityEngine.Vector3;


public class plane_Reflection_camera : MonoBehaviour
{
    [ExecuteAlways]
    public Camera Main_camera;
    public Camera Reflection_camera;
    public GameObject plane;//定义平面组件的信息;

    private readonly RenderTexture _Reflection_camera_RT;//定义反射RT;
    private int _Reflection_camera_RT_ID;//定义主帖图_MainTex的ID;
    public Material Reflection_material;//传入材质;

    public Shader shader;//定义Shader;
    //在脚本Start时候我们要先判断当前无Reflection_camera,如果没有则我们要获取新组件,并且设置获取的为摄像机的组件,最后还要
    //RenderPipelineManager.beginCameraRendering += OnBeginCameraRendering来订略事件;
    void Start()//Start函数在脚本开始时运行;
    {
        Debug.Log("Plane Reflection Success");
        if (Reflection_camera == null)
        {
//下面获取新的摄像机组件,并传给Reflection_camera;
            var new_Gameogject = new GameObject("Reflection camera");//获取新的组件;
            this.Reflection_camera = new_Gameogject.AddComponent<Camera>();//这里设置新的组件为摄像机组件;
            
        }

        RenderPipelineManager.beginCameraRendering += OnBeginCameraRendering;//加上渲染事件,这一个会在接下来进行定义;
        //不要忘了加上渲染事件;
    }

    void OnBeginCameraRendering(ScriptableRenderContext context ,Camera camera)//这一个函数传入context,以及摄像机;
    {
        //1.首先会进行判断,判断传入的摄像机是否为反射的摄像机;
        //2.先更新这一个摄像机,然后清除刚定义的摄像机的DepthBuffer以及ColorBuffer,用backgroung属性进行替代;
        //3.再清除背景颜色,并同时确定摄像机的渲染层;
        if (camera == this.Reflection_camera)
        {
            Update_camera(this.Reflection_camera);//首先更新新创建的refection_camera;
            camera.clearFlags = CameraClearFlags.SolidColor;//清除刚定义的摄像机的DepthBuffer以及ColorBuffer,用backgroung属性进行替代;
            camera.backgroundColor = Color.clear;//清除背景颜色;
            camera.cullingMask = LayerMask.GetMask("reflect");//获取渲染层;
            
            //接下来会构建反射矩阵,将摄像机转移到世界空间坐标下,并同时进行裁剪顺序的翻转;
            var Reflection_camera_M = CalculateReflectionCameraMatrix(this.plane.transform.up, this.plane.transform.position);
            GL.invertCulling = true;//进行裁剪顺序的翻转;//将裁剪顺序翻转回去,因为反射矩阵的变化会引起裁剪顺序的变化
            
            //在进行摄像机的渲染之前,要进行视锥体的裁剪;!!
            
            
            //接下来是摄像机开始渲染的部分;1.首先我们会利用UniversalRenderPipeline.RenderSingleCamera函数来进行开启摄像机;
            //2.我们会进行屏幕截图获取一张RT贴图;
            //3.我们会获取着色器属性名称_Reflection_camera_RT的唯一标识符_Reflection_camera_RT_ID ;
            //4.利用Shader的SetGlobalTexture函数,以及ID,和着色器设置全局纹理;
            //5.设置渲染纹理为设想的摄像机的渲染纹理,之后反射相机的输出就会到我们的临时纹理RT上;
            //6.将贴图传入材质,并同时释放全局纹理;

            UniversalRenderPipeline.RenderSingleCamera(context,camera);//摄像机开始渲染;
            RenderTexture Reflection_camera_temporary_RT = RenderTexture.GetTemporary(Screen.width,Screen.height,0);//传入屏幕的狂高获取一张截图;
            _Reflection_camera_RT_ID = Shader.PropertyToID("_Reflection_camera_RT_ID");//获取ID;
            Shader.SetGlobalTexture(_Reflection_camera_RT_ID,_Reflection_camera_RT);//为着色器设置全局纹理;
            camera.targetTexture = Reflection_camera_temporary_RT;//反射摄像机的输出就是这一张纹理;
            Reflection_material.SetTexture(_Reflection_camera_RT_ID,Reflection_camera_temporary_RT);//将贴图传入到我们定义的材质中;
            //最后释放RT临时纹理;
            RenderTexture.ReleaseTemporary(Reflection_camera_temporary_RT);//释放临时纹理RT;

        }
        else
        {
            GL.invertCulling = false;//如果判断不为反射的摄像机则不会执行这一步;
        }
    }
    
    //下面还要进行顶略事件的取消,以及同步两个相机,相当于反射相机的初始化;
    private void OnDiable()
    {
        RenderPipelineManager.beginCameraRendering -= OnBeginCameraRendering;
    }
    //进行摄像机的同步操作;
    private void Update_camera(Camera Reflection_camera)//同步两个摄像机的数据;
    {
        //先同步两个摄像机的数据,初始化完Reflection_camera后立刻背景颜色和深度的清除,然后设置在相机开始渲染前的各种设置
        //1.如果两个摄像机都不存在则返回;
        if (Main_camera == null || this.Reflection_camera == null)
        {
            return;
        }

        //2.
        int target_display = Reflection_camera.targetDisplay;
        Reflection_camera.CopyFrom(Main_camera);
        Reflection_camera.targetDisplay = target_display;

    }


    //获取摄像机的一个反射矩阵; 
    //重在理解如何获取反射矩阵;
    private Matrix4x4 CalculateReflectionCameraMatrix(Vector3 N, Vector3 plane_position)
    {
        //先初始化反射矩阵;
        Matrix4x4 Reflection_camera_M = Matrix4x4.identity;
        //获取反射矩阵中的d;
        float d = -Vector3.Dot(plane_position, N);
        Reflection_camera_M.m00 = 1 - 2 * N.x * N.x;
        Reflection_camera_M.m01 = -2 * N.x * N.y;
        Reflection_camera_M.m02 = -2 * N.x * N.z;
        Reflection_camera_M.m03 = -2 * N.x * d;

        Reflection_camera_M.m10 = -2 * N.y * N.x;
        Reflection_camera_M.m11 = 1 - 2 * N.y * N.y;
        Reflection_camera_M.m12 = -2 * N.y * N.z;
        Reflection_camera_M.m13 = -2 * N.y * d;

        Reflection_camera_M.m20 = -2 * N.z * N.x;
        Reflection_camera_M.m21 = -2 * N.z * N.y;
        Reflection_camera_M.m22 = 1 - 2 * N.z * N.z;
        Reflection_camera_M.m23 = -2 * N.z * d;

        Reflection_camera_M.m30 = 0;
        Reflection_camera_M.m31 = 0;
        Reflection_camera_M.m32 = 0;
        Reflection_camera_M.m33 = 1;
        
        return Reflection_camera_M;

    }
}

一些脚本的函数解释

Camera.targetDisplay

urp镜面,Unity学习笔记,unity,平面,游戏引擎,Powered by 金山文档

Camera.clearFlags

urp镜面,Unity学习笔记,unity,平面,游戏引擎,Powered by 金山文档

CommandBuffer.GetTemporaryRT

urp镜面,Unity学习笔记,unity,平面,游戏引擎,Powered by 金山文档

该函数不仅申请了一个临时rt,同时还将该rt与第一个参数"nameID"所代表的全局的ShaderProperty进行了绑定,也就是说之后的shader都可以用与nameID对应的名称使用该rt。

官方的urp渲染管线中,CopyColorPass就是用该函数将"_CameraColorTexture"这个纹理拷贝到临时rt中供之后的shader使用。

坑点:在调用CommandBuffer.ReleaseTemporaryRT进行释放并不是将该纹理清空了,该纹理依旧在内存中,如果在之后重新调用CommandBuffer.GetTemporaryRT申请一个大小格式相同的临时纹理,会拿到该纹理,也就是说CommandBuffer.GetTemporaryRT得到的不一定是一张干净的纹理,很有可能是已经被写过的,所以必要的时侯要进行clear。其次,如果发生了上述这种回收重用的情况,rt的名称可能会错乱,也就是说尽管shaderproperty的绑定逻辑是没有问题,但是rt的name没有相应的更新,在framedebugger中会让人十分困惑,例如两个相机,分别在afterRenderingTransparent之后进行了一次全屏copyblit,前者给了_Transparent的rt,后者给了_UITranspaernt的rt,但是之后发现在前一个相机渲染中,物体shader中_Transparent获取的纹理名称叫_UITranspaernt,在使用renderdoc会发现两次全屏copyblit的目标纹理使用的是同一个rt,自始至终只有一个叫_UITranspaernt的rt,该rt仅仅是在过程中更换一下和shaderproperty的绑定,因此shader中的_Transparent和_UITranspaernt都会得到该rt。虽然在逻辑上没有错,最终结果是正确的,但是会给debug过程中带来困扰,如果想要规避这种情况可以显示地使用new RenderTexture创建rt。

还有平面Shader的代码:

Shader "Unlit/PlanerReflection_shader"
{
    Properties
    {
        _Diffuse("Diffuse", Color) = (1, 1, 1, 1)
        _ReflectColor("_ReflectColor", Color) = (1, 1, 1, 1)
        _ReflectAmount("_ReflectAmount", Range(0,1)) = 1
        _Reflection_CubeMap("_Reflection_CubeMap", Cube) = "_Skybox"{}
        
        _Fresnel("Fresnel", Float) = 1.0
        
        _Specular("Specular", Color) = (1, 1, 1, 1)
        _Gloss("Gloss", Range(8,255)) = 8.0  
        
    }
    SubShader
    {
        Tags {"Queue" = "Transparent" "RenderType" = "Transparent" "RenderPipeline" = "UniversalPipeline"}
        Pass
        {
        Tags {"LightMode" = "UniversalForward" }
            
            
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS //接收阴影
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE //得到正确的阴影坐标
            #pragma multi_compile _ _SHADOWS_SOFT //软阴影

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl"
            
            
            CBUFFER_START(UnityPerMaterial)
            half4 _Diffuse;
            float4 _ReflectColor;//用于控制反射的天空盒子的颜色量
            float _ReflectAmount;//用于控制反射的天空盒子的颜色和漫反射diffuse在总体反射中的占比
            samplerCUBE _Reflection_CubeMap;
            

            float _Fresnel;
            
            half4 _Specular;
            half _Gloss;

            TEXTURE2D(_Reflection_camera_RT);
            SamplerState sampler_Reflection_camera_RT;//采样采样设置

            TEXTURE2D(_Reflection_Scene_camera_RT);
            SamplerState sample_Reflection_Scene_camera_RT;
            
            CBUFFER_END
            
            struct appdata
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                //float2 uv: TEXCOORD0;
            };

            struct v2f
            {
                float4 position_CS : SV_POSITION;//裁剪空间的坐标
                float4 screenPos : TEXCOORD0;//屏幕坐标
                float4 position_WS : TEXCOORD1;//顶点位置坐标
                float3 view_direct_WS : TEXCOORD2;//人眼光路的入射方向
                float3 reflec_direct_WS : TEXCOORD3;//人眼光路反射后的方向,用于采样天空盒
                float3 normal_WS : TEXCOORD4;//顶点在世界空间的法线方向
                
            };

            
            v2f vert (appdata v)
            {
                v2f o;
                o.position_CS = TransformObjectToHClip(v.vertex);//获得顶点在裁剪空间的坐标
                o.position_WS = mul(unity_ObjectToWorld, v.vertex);
                o.screenPos = ComputeScreenPos(o.position_CS);//计算用于采样反射贴图的顶点
                o.normal_WS = TransformObjectToWorldNormal(v.normal);//计算世界空间下的法线方向
                o.view_direct_WS = GetWorldSpaceViewDir(o.position_WS);//获取世界空间下的人眼光路的入射方向,这里获取的方向是从顶点指向摄像机
                o.reflec_direct_WS = reflect(-normalize(o.view_direct_WS),normalize(o.normal_WS));//获取世界空间下人眼光路的反射光线
                return o;
            }

            half4 frag (v2f i) : SV_Target
            {
                float4 SHADOW_COORDS = TransformWorldToShadowCoord(i.position_WS);//获取将世界空间的顶点坐标转换到光源空间获取的阴影坐标,这里在片元着色器里面进行,利用了插值之后的结果
                Light main_light = GetMainLight(SHADOW_COORDS);
                
                float3 light_direction_WS = normalize(TransformObjectToWorld(main_light.direction));//获取世界空间的光照单位矢量
                float3 view_direct_WS = normalize(i.view_direct_WS);
                float3 reflec_direct_WS = normalize(i.reflec_direct_WS);
                float3 normal_WS = normalize(i.normal_WS);
                
                half3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;//获取环境光强度
                
                half3 diffuse = main_light.color.rgb * _Diffuse.rgb * saturate(dot(normal_WS, light_direction_WS));//获取漫反射强度,light.color.rgb是光照强度
                half3 reflection = texCUBE(_Reflection_CubeMap,reflec_direct_WS).rgb * _ReflectColor.rgb;//获取反射的天空盒子的颜色
                
                half3 half_dir = normalize(light_direction_WS + view_direct_WS);//获取半程向量
                half3 specular = main_light.color.rgb * _Specular.rgb * pow(saturate(dot(half_dir,normal_WS)), _Gloss);//获取高光

                
                reflection = lerp(diffuse, reflection,  _ReflectAmount);//通过平滑函数获得最佳的反射
                
                float fresnel = pow(1 - dot(normal_WS, view_direct_WS), _Fresnel);
                
                
                
                half4 reflection_map_color = _Reflection_camera_RT.Sample(sampler_Reflection_camera_RT, i.screenPos.xy / i.position_CS.w);//采样反射纹理贴图
                half4 color = half4(ambient + fresnel * reflection  + specular, 1.0);
                
                color = reflection_map_color + color;
                return color;
            }
            ENDHLSL
        }
    }
    Fallback off
}

一些设置

记住我们在显示物体的镜面效果时,要将物体最上面的渲染层新建一个或选择一个与上述我们自己定义反射摄像机的渲染层相同的渲染层:

urp镜面,Unity学习笔记,unity,平面,游戏引擎,Powered by 金山文档

capsule

此处的Layer层要与我们在摄像机里面的相同,上面我们设置为“reflect”,因此这里我们自己创建的时候也要命名为"reflect"!!;

下面是反射摄像机的设置:

urp镜面,Unity学习笔记,unity,平面,游戏引擎,Powered by 金山文档

在Rendering里面的Culling Mask要设置为reflect;

下面是效果图:

urp镜面,Unity学习笔记,unity,平面,游戏引擎,Powered by 金山文档
urp镜面,Unity学习笔记,unity,平面,游戏引擎,Powered by 金山文档

注意到此处我们还没有加入视锥体的裁剪,因此效果很奇怪,下面我们会加入;

视锥体的裁剪

如下图,视锥体的A部分应该被裁剪掉;

urp镜面,Unity学习笔记,unity,平面,游戏引擎,Powered by 金山文档

具体实现方法看这一篇文章:

https://zhuanlan.zhihu.com/p/493766119

需要注意的是我们应该将其放在订阅的时间内,在摄像机开始进行渲染之前;

简单概括,这种技术通过改变 MVP 中的 P 矩阵,实现用指定的平面来当作近平面,但同时会影响到远平面,而其他四个平面不受影响,

我们先用四维向量来对平面进行表示,再将世界空间中的平面表示转到摄像机空间,之后我们会利用Reflection_camera中的CalculateObliqueMatrix函数来设置反射平面成为近平面的矩阵,最后会将这一个透视投影矩阵传入到Reflection_camera的projectionMatrix中进行之后的渲染操作,代码如下:

 Vector4 viewPlane = new Vector4(this.Plane.transform.up.x, this.Plane.transform.up.y,this.Plane.transform.up.z,
                -Vector3.Dot(this.Plane.transform.position, this.Plane.transform.up));//获取平面的四维表示;
            viewPlane = Reflection_camera.worldToCameraMatrix.inverse.transpose * viewPlane;//将世界空间中的平面表示传到摄像机空间;
            var newMatrix = Reflection_camera.CalculateObliqueMatrix(viewPlane);//这里获取以摄像机以反射平面为近平面的矩阵;
            Reflection_camera.projectionMatrix = newMatrix;
urp镜面,Unity学习笔记,unity,平面,游戏引擎,Powered by 金山文档

这是最后的效果,很明显已经解决了视锥体的问题;

到此就是平面反射的全部内容,其他知识还待以后学习;文章来源地址https://www.toymoban.com/news/detail-702124.html

到了这里,关于Unity URP 平面反射笔记的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Unity中URP下的SimpleLit的 Lambert漫反射计算

    在之前的文章中,我们已经知道了 SimpleLit 下的主光数据怎么获取。 Unity中URP下获取主灯信息 Unity中ShaderGraph下获取主灯 有了这些数据,我们就可以计算 Lambert漫反射 和 BlinnPhone高光反射 了。 我们在获取了主光信息后 Light mainLight = GetMainLight1(inputData, shadowMask, aoFactor); 就来到了

    2024年01月21日
    浏览(93)
  • Unity3D学习记录01:URP渲染管线以及3D游戏场景设置

    以下内容所使用的版本均为Unity2022.3 先在 Window-Package Manager-Unity Registry 里面搜索添加Universal RP   Unity中,创建渲染管线的方式为Asset文件夹下右键 Create-Readering-URP Asset(with Universal Asset) 会创建以下两个Pipeline:  接着在图中的设置里添加这两个渲染管线(Project Setting在Edit窗口下

    2024年02月08日
    浏览(63)
  • Unity SRP 管线【第七讲:URP LOD实现以及Reflections反射探针】

    中文版:https://edu.uwa4d.com/lesson-detail/282/1314/0?isPreview=0 英文原版:https://catlikecoding.com/unity/tutorials/custom-srp/lod-and-reflections/ 1. 首先该组件需要将子类模型置于该组件物体子节点下 2. 可在单个LOD中设置其level的模型,并可设置它的距离范围,即可在不同距离下显示不同的模型 若

    2024年02月20日
    浏览(40)
  • Unity Shader 学习笔记(4)URP渲染管线带阴影PBR-Shader模板 -- 新增可自定义阴影颜色

    材质面板截图 功能实现(URP渲染管线下): 1、进一步优化Shader结构和算法; 2、包含PBR材质; 3、投射和接收阴影,并升级 支持自定义阴影颜色 ; 4、支持点光源照射(但不支持点光源阴影)。 通用渲染截图 自定义阴影颜色截图 完整代码: 写在最后: 1、在我的上一篇文

    2024年02月12日
    浏览(50)
  • [Unity/URP学习]风格化水体渲染(一)

    着色:水体颜色、水体反射、水体折射、岸边泡沫、水面于天空沿边线消除、水体焦散 动画处理:水体流动、顶点动画、水体交互、水体浮力 (实现顺序没有严格按照着色和动画处理的分类来实现) 要制作水体颜色,要考虑的内容如下: 风格化的水体渐变颜色、水体深浅区

    2024年02月13日
    浏览(57)
  • 【游戏开发小技】Unity中实现Dota里的角色技能地面贴花效果(URP ShaderGraph Decal)(1)

    [Toggle(_SupportOrthographicCamera)] _SupportOrthographicCamera(“_SupportOrthographicCamera (default = off)”, Float) = 0 } SubShader { // 关于tags的内容可以查阅官网手册:https://docs.unity3d.com/Manual/SL-SubShaderTags.html // 为了避免渲染顺序问题, Queue必须 = 2501, 它位于透明队列中 // 在透明队列中,Unity总是从后

    2024年04月16日
    浏览(56)
  • 【Unity URP】URP Camera使用及动态设置

    官方文档:   摄像机 | Universal RP | 12.1.1 https://docs.unity3d.com/cn/Packages/com.unity.render-pipelines.universal@12.1/manual/cameras.html 相机类型: Render Type(渲染类型) Base(基础相机):渲染到特定渲染目标的通用相机,场景中必须至少有一个。 Overlay(叠加相机):将其视图渲染在另一个摄

    2024年02月12日
    浏览(46)
  • Unity 基础之 URP 项目创建\项目转URP Pipline

    目录 Unity 基础之 URP 项目创建项目转URP Pipline 一、简单介绍 二、创建 URP 项目 三、工程项目转 URP Unity中的一些基础知识点,方便日后查阅。 Unity游戏开发中,这里简单介绍如何创建 URP 工程项目,和把已有项目转为 URP 项目的过程,这里做简单记录,如果有不对,欢迎指出。

    2024年02月16日
    浏览(48)
  • Unity——URP相机详解

    2021版本URP项目下的相机,一般新建一个相机有如下组件 1:Render Type( 渲染类型 ) 有Base和Overlay两种选项,默认是Base选项         Base:主相机使用该种渲染方式,负责渲染场景中的主要图形元素         Overlay(叠加):使用了Oveylay的相机会把它渲染出的图形叠加到其他相机之上

    2024年02月05日
    浏览(53)
  • 【Unity URP】手写PBR:从build-in转到URP

    写在前面 后续要在URP下实现PBR+NPR的风格化渲染,所以这里要赶紧把之前手写的PBR挪到URP管线下。由于URP各个版本更新换代太快了,贴一下项目环境,给后面看到这篇文章的小伙伴提个醒,我的项目环境: URP12.1.7 Unity2021.3.8f1 整体框架几乎一样吧,目前先实现主光源的部分,

    2024年02月14日
    浏览(81)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包