OpenGL學(xué)習(xí)28——陰影映射

陰影映射(Shadow Mapping)

陰影是遮擋導(dǎo)致光線缺失造成的。陰影能夠讓我們的場(chǎng)景變得更加真實(shí),也能讓我們更容易地觀察物體在空間中的關(guān)系。

1. 陰影映射

  • 陰影映射(shadow mapping) 的思想很簡(jiǎn)單:就是我們將光源位置作為視角來(lái)渲染場(chǎng)景,光線照射到的所有物體都是可見(jiàn),其他則都處于陰影之中。我們看下圖展示的場(chǎng)景:(圖片取自書(shū)中
    一個(gè)陰影場(chǎng)景
  • 上圖中,所有藍(lán)色線代表光源能照射到的片元,被遮擋的片元?jiǎng)t用黑色線表示。對(duì)于陰影計(jì)算,一般,我們需要獲取光線最先撞擊物體時(shí)的點(diǎn),并將這個(gè)最近點(diǎn)與光線上的其他點(diǎn)進(jìn)行比較。如果測(cè)試點(diǎn)比最近點(diǎn)遠(yuǎn)則處于陰影中。在場(chǎng)景中迭代所有可能的光線進(jìn)行計(jì)算對(duì)于實(shí)時(shí)渲染來(lái)說(shuō)太過(guò)耗時(shí),因此我們采取相似的方法,只是我們不投射光線而是使用深度緩沖區(qū)來(lái)做替代。
  • 深度測(cè)試章節(jié)我們知道深度緩沖區(qū)中的深度值代表片元的深度,并且被限制在[0, 1]之間。如果我們從光源的角度渲染場(chǎng)景并將深度值結(jié)果存儲(chǔ)到一個(gè)紋理中,那么我們就能從光源的角度采樣最近的深度值。因?yàn)樯疃染彌_區(qū)中的深度值就是第一個(gè)可見(jiàn)的片元的深度值。我們將存儲(chǔ)這些深度值的紋理稱(chēng)為深度圖(depth map)陰影圖(shadow map)。(圖片取自書(shū)中
    陰影圖/陰影計(jì)算
  • 上圖我們展示了一個(gè)定向光源在一個(gè)立方體下方投射陰影的場(chǎng)景。在右側(cè)圖中,假設(shè)我們要渲染點(diǎn)\color{red}{\overline{P}}上的片元。那么要確定\color{red}{\overline{P}}是否處于陰影中,我們首先使用矩陣T將點(diǎn)\color{red}{\overline{P}}轉(zhuǎn)換到光源空間。當(dāng)轉(zhuǎn)換到光源空間后,點(diǎn)\color{red}{\overline{P}}z值就是深度值(圖中為0.9)。然后我們使用陰影圖索引光源的最近點(diǎn),圖中為\color{green}{\overline{C}},深度值為0.4。因?yàn)樽罱c(diǎn)的深度值小于點(diǎn)\color{red}{\overline{P}}深度值,我們可以確定點(diǎn)\color{red}{\overline{P}}處于陰影中。
  • 陰影映射的兩個(gè)階段:
      1. 渲染深度圖。
      1. 渲染場(chǎng)景并使用深度圖計(jì)算片元是否處于陰影中。

2. 深度圖

深度圖就是我們從光源的視角渲染場(chǎng)景所生成的紋理。因?yàn)槲覀冃枰獙?chǎng)景的渲染結(jié)果存儲(chǔ)到紋理中,因此我們需要使用到幀緩沖區(qū),下面是創(chuàng)建深度圖的主要步驟:

    1. 創(chuàng)建用于生成深度圖的幀緩沖區(qū)對(duì)象。
unsigned int depthMapFBO;
glGenFramebuffers(1, & depthMapFBO);
    1. 創(chuàng)建用作幀緩沖區(qū)的深度緩沖區(qū)的2D紋理。
// 深度圖分辨率:1024x1024
const unsigned int SHADOW_WIDTH = 1024, SHADOW_HEIGHT = 1024;

unsigned int depthMap;
glGenTextures(1, & depthMap);
glBindTexture(GL_TEXTURE_2D, depthMap);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    1. 將紋理對(duì)象附加為幀緩沖區(qū)的深度緩沖區(qū)。
glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
  • 因?yàn)閹彌_區(qū)沒(méi)有顏色緩沖區(qū)是不完整的,所以上面我們使用GL_NONE參數(shù)調(diào)用glDrawBufferglReadBuffer函數(shù),顯式告訴OpenGL我們不渲染任何顏色數(shù)據(jù)。
  • 配置好幀緩沖區(qū)和紋理,我們就可以進(jìn)行深度圖生成。整個(gè)渲染過(guò)程的偽代碼如下:
// 1. 渲染深度圖
glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT);
glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO);
glClear(GL_DEPTH_BUFFER_BIT);
ConfigureShaderAndMatrices();
RenderScene();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// 2. 使用深度圖渲染場(chǎng)景
glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
ConfigureShaderAndMatrices();
glBindTexture(GL_TEXTURE_2D, depthMap);
RenderScene();

2.1 光源空間變換

  • 在上面?zhèn)未a的第一階段,我們還需完善函數(shù)ConfigureShaderAndMatrices。在這里我們需要設(shè)置合適的視矩陣和投影矩陣,將場(chǎng)景轉(zhuǎn)換為以光源為視角。因?yàn)槲覀円獙?duì)定向光源進(jìn)行建模,所以我們采用正射投影:
float near_plane = 1.0f, far_plane = 7.5f;
glm::mat4 lightProjection = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane);
  • 對(duì)于視矩陣,我們使用glm::lookAt函數(shù):
glm::mat4 lightView = glm::lookAt(glm::vec3(-2.0f, 4.0f, -1.0f),
                                  glm::vec3( 0.0f, 0.0f,  0.0f),
                                  glm::vec3( 0.0f, 1.0f,  0.0f));
  • 將視矩陣和投影矩陣結(jié)合,我們就可以將世界空間的矢量轉(zhuǎn)換到光源空間,這個(gè)結(jié)合矩陣就是我們前面提到的矩陣T。
glm::mat4 lightSpaceMatrix = lightProjection * lightView;

2.2 渲染深度圖

  • 對(duì)于深度圖,我們只關(guān)心深度值,而不關(guān)心片元的顏色計(jì)算。為了提高性能,我們?yōu)樯疃葓D提供獨(dú)立且更簡(jiǎn)單的著色器。頂點(diǎn)著色器如下:
#version 330 core
layout (location = 0) in vec3 aPos;

uniform mat4 lightSpaceMatrix;
uniform mat4 model;

void main()
{
    gl_Position = lightSpaceMatrix * model * vec4(aPos, 1.0);
}
  • 因?yàn)槲覀儧](méi)有使用顏色緩沖區(qū),也禁用了緩沖區(qū)的讀寫(xiě),因此使用一個(gè)空的片元著色器。
#version 330 core

void main()
{
    // gl_FragDepth = gl.FragCoord.z;
}
  • 深度圖的渲染偽代碼變?yōu)椋?/li>
simpleDepthShader.use();
glUniformMatrix4fv(lightSpaceMatrixLocation, 1, GL_FALSE, glm::value_ptr(lightSpaceMatrix));
glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT);
glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO);
glClear(GL_DEPTH_BUFFER_BIT);
RenderScene(simpleDepthShader);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
  • 我們使用如下片元著色器將深度圖顯示到屏幕。
#version 330 core
out vec4 FragColor;

in vec2 TexCoords;

uniform sampler2D depthMap;

void main()
{
    float depthValue = texture(depthMap, TexCoords).r;
    FragColor = vec4(vec3(depthValue), 1.0);
}
  • 深度圖渲染效果。


    深度圖

3. 陰影渲染

  • 當(dāng)生成深度圖后,我們就可以渲染實(shí)際的陰影。雖然檢查片元是否處于陰影中是在片元著色器中,但是我們需要在頂點(diǎn)著色器中將場(chǎng)景轉(zhuǎn)換為以光源為視角。在頂點(diǎn)著色器中我們?cè)黾右粋€(gè)輸出FragPosLightSpace,即轉(zhuǎn)換為光源視角的頂點(diǎn)的坐標(biāo)。
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;

out VS_OUT
{
    vec3 FragPos;
    vec3 Normal;
    vec2 TexCoords;
    vec4 FragPosLightSpace;
} vs_out;

uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
uniform mat4 lightSpaceMatrix;

void main()
{
    vs_out.FragPos = vec3(model * vec4(aPos, 1.0));
    vs_out.Normal = transpose(inverse(mat3(model))) * aNormal;
    vs_out.TexCoords = aTexCoords;
    vs_out.FragPosLightSpace = lightSpaceMatrix * vec4(vs_out.FragPos, 1.0);
    gl_Position = projection * view * model * vec4(aPos, 1.0);
}
  • 片元著色器中我們使用Blinn-Phong模型。對(duì)于陰影我們計(jì)算出一個(gè)值shadow,為1.0則片元處于陰影中,0.0則不處于陰影中。然后我們使用該陰影值乘以擴(kuò)散光和鏡面光分量。
#version 330 core
out vec4 FragColor;

in VS_OUT
{
    vec3 FragPos;
    vec3 Normal;
    vec2 TexCoords;
    vec4 FragPosLightSpace;
} fs_in;

uniform sampler2D diffuseTexture;
uniform sampler2D shadowMap;

uniform vec3 lightPos;
uniform vec3 viewPos;

float ShadowCalculation(vec4 fragPosLightSpace)
{
    ...
}

void main()
{
    vec3 color = texture(diffuseTexture, fs_in.TexCoords).rgb;
    vec3 normal = normalize(fs_in.Normal);
    vec3 lightColor = vec3(0.3);
    // ambient
    vec3 ambient = 0.15 * color;
    // diffuse
    vec3 lightDir = normalize(lightPos - fs_in.FragPos);
    float diff = max(dot(lightDir, normal), 0.0);
    vec3 diffuse = diff * lightColor;
    // specular
    vec3 viewDir = normalize(viewPos - fs_in.FragPos);
    float spec = 0.0;
    vec3 halfwayDir = normalize(lightDir + viewDir);
    spec = pow(max(dot(normal, halfwayDir), 0.0), 64.0);
    vec3 specular = spec * lightColor;
    // caculate shadow
    float shadow = ShadowCalculation(fs_in.FragPosLightSpace);
    vec3 lighting = (ambient + (1.0 -shadow) * (diffuse + specular)) * color;

    FragColor = vec4(lighting, 1.0);
}
  • 判斷片元是否處于陰影要做的第一件事就是將光源視角下裁剪空間的片元坐標(biāo)轉(zhuǎn)換為標(biāo)準(zhǔn)設(shè)備坐標(biāo)。當(dāng)我們通過(guò)頂點(diǎn)著色器的gl_Position輸出裁剪空間的頂點(diǎn)坐標(biāo),OpenGL自動(dòng)執(zhí)行透視除法——通過(guò)將坐標(biāo)的x, y, z分量除以w分量把裁剪空間[-w, w]映射到[-1, 1]。但是我們光源視角的片元位置FragPosLightSpace不是通過(guò)gl_Position輸出,因此我們需要自己完成透視除法。
float ShadowCalculation(vec4 fragPosLightSpace)
{
    // 透視除法
    vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
    ...
}
  • 注意:如果使用正射投影,那么執(zhí)行透視除法沒(méi)有任何意義,不過(guò)保留這行代碼可以讓我們的方法同時(shí)兼容正射投影和透視投影。
  • 因?yàn)殛幱皥D的范圍為[0, 1],而我們使用projCoords坐標(biāo)來(lái)對(duì)陰影圖進(jìn)行采樣,因此我們還需要將其轉(zhuǎn)換到范圍為[0, 1]的NDC坐標(biāo)。
projCoords = projCoords * 0.5 + 0.5;
  • 使用上述投影坐標(biāo)我們就可以從陰影圖進(jìn)行采樣,獲取以光源視角的最近深度值。
float closestDepth = texture(shadowMap, projCoords.xy).r;
  • 獲取片元的深度值我們直接取坐標(biāo)的z分量,那么片元是否處于陰影的判斷就是對(duì)片元深度值與最近深度值的進(jìn)行比較。
float currentDepth = projCoords.z;
float shadow = currentDepth > closestDepth ? 1.0 : 0.0;
  • 完整的陰影計(jì)算如下:
float ShadowCalculation(vec4 fragPosLightSpace)
{
    // 透視除法
    vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
    // 轉(zhuǎn)換坐標(biāo)到[0, 1]
    projCoords = projCoords * 0.5 + 0.5;
    // 獲取光源視角下最近深度值
    float closestDepth = texture(shadowMap, projCoords.xy).r;
    // 獲取光源視角下當(dāng)前片元深度值
    float currentDepth = projCoords.z;
    // 判斷當(dāng)前片元是否處于陰影
    float shadow = currentDepth > closestDepth ? 1.0 : 0.0;

    return shadow;
}
陰影渲染1

4. 優(yōu)化陰影渲染

4.1 陰影粉刺

陰影粉刺
  • 上圖的渲染中我們可以在地板上看到明顯相間的黑色條紋。由于陰影映射而產(chǎn)生的偽影我們稱(chēng)為陰影粉刺(shadow acne)。陰影粉刺可由下圖進(jìn)行解釋?zhuān)海?strong>圖片取自書(shū)中)
    陰影粉刺產(chǎn)生示意圖
  • 陰影圖因?yàn)榉直媛实南拗?,?dāng)片元與光源距離相對(duì)較遠(yuǎn)時(shí),多個(gè)片元可能從陰影圖采樣到相同的值。如上圖所示,每個(gè)黃色傾斜面板代表陰影圖中的一個(gè)紋理元(texel),多個(gè)片元會(huì)采樣到相同的深度值。這種情形一般是可以接受的,但是當(dāng)光源以一定角度照射地板表面,那么陰影圖也是以相對(duì)于表面一定角度進(jìn)行渲染的。這時(shí)多個(gè)片元從相同的傾斜面板進(jìn)行采樣,那些處于上方的形成陰影,處于下方則被照亮,從而在地板表面形成相間黑色條紋。
  • 要解決上述問(wèn)題,我們可以采用一個(gè)小技巧稱(chēng)為陰影偏移(shadow bias),我們簡(jiǎn)單對(duì)表面的深度進(jìn)行小量偏移,這樣片元就不會(huì)被錯(cuò)認(rèn)在表面之下。如下圖所示:(圖片取自書(shū)中
    陰影偏移
  • 應(yīng)用陰影偏移,所有表面的片元獲得一個(gè)較小的深度值,相當(dāng)于把陰影圖整個(gè)往上移動(dòng),這樣就能消除表面的陰影條紋。
float bias = 0.005;
float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0;
  • 雖然應(yīng)用簡(jiǎn)單的陰影偏移可以解決我們的大部分問(wèn)題,但是我們?cè)O(shè)置的偏移值獨(dú)立于光源和表面的角度,如果角度很陡峭,仍然可能產(chǎn)生陰影粉刺。一種更可靠的解決方法是我們基于光源與表面的角度來(lái)調(diào)整偏移量,而利用點(diǎn)積我們可以實(shí)現(xiàn)這一點(diǎn):
float bias = max(0.05 * (1.0 - dot(normal, lightDir)), 0.005);
  • 渲染效果。


    陰影偏移渲染

4.2 彼得平移(peter panning)

  • 陰影偏移的一個(gè)缺點(diǎn)就是我們將偏移應(yīng)用到物體的實(shí)際深度值,當(dāng)偏移值過(guò)大時(shí)會(huì)造成相對(duì)于物體的實(shí)際位置可見(jiàn)的陰影錯(cuò)位,如下圖所示:


    彼得平移
  • 上圖中的陰影偽影稱(chēng)為彼得平移(peter panning),因?yàn)槲矬w看起來(lái)與陰影分離。我們可以通過(guò)在渲染深度圖時(shí)使用面剔除來(lái)解決大部分彼得平移問(wèn)題。因?yàn)樵谏疃葓D渲染中我們只需要深度值,因此對(duì)于實(shí)心物體來(lái)說(shuō),我們?nèi)∥矬w的前向表面深度值或背向表面深度圖不會(huì)產(chǎn)生影響。見(jiàn)下圖:(圖片取自書(shū)中
    深度圖的面剔除
  • 要解決彼得平移,我們?cè)谏疃葓D生成時(shí)剔除所有前向面片。
glCullFace(GL_FRONT);
RenderSceneToDepthMap();
glCullFace(GL_BACK);
...

4.3 過(guò)采樣

  • 另外一個(gè)場(chǎng)景失真的情況是處于光源可見(jiàn)截錐之外的區(qū)域都會(huì)被當(dāng)作處于陰影之中,而實(shí)際上可能不是。這是因?yàn)楣庠唇劐F之外的投影坐標(biāo)大于1.0,因此對(duì)深度圖采樣其值將在[0, 1]范圍之外。前面我們?cè)O(shè)置了深度圖紋理時(shí)使用了紋理扭曲選項(xiàng)GL_REPEAT,因此截錐之外的不是基于真實(shí)深度值進(jìn)行采樣。
  • 針對(duì)上述問(wèn)題,我們一般將深度圖范圍外的的深度值都設(shè)置為1.0,我們可以通過(guò)將紋理扭曲選項(xiàng)設(shè)置為GL_CLAMP_TO_BORDER來(lái)完成。
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
float borderColor[] = { 1.0f, 1.0f, 1.0f, 1.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
  • 使用這樣的紋理設(shè)置,當(dāng)我們采樣處于深度圖[0, 1]范圍外的值總是返回1.0,這樣陰影值就為0.0。渲染結(jié)果如下:


    截錐外陰影區(qū)域
  • 上圖的渲染結(jié)果還存在一個(gè)陰暗區(qū)域,這是因?yàn)橛行┳鴺?biāo)處于光源正射投影截錐遠(yuǎn)平面之外。當(dāng)坐標(biāo)的z分量值大于1.0時(shí),以光源視角投射產(chǎn)生的坐標(biāo)將比遠(yuǎn)平面還遠(yuǎn)。這時(shí)候紋理設(shè)置GL_CLAMP_TO_BORDER將不起作用,因?yàn)閦分量大于1.0總是比深度值大。解決該問(wèn)題的一種簡(jiǎn)單方法就是當(dāng)z分量大于1.0時(shí)我們強(qiáng)制將陰影值設(shè)置為0.0。
float ShadowCaculation(vec4 fragPosLightSpace)
{
    ...
    if(projCooord.z > 1.0)
        shadow = 0.0;
    
    return shadow;
}
  • 最終渲染結(jié)果。


    陰影過(guò)采樣優(yōu)化

4. PCF

  • 雖然目前得到的陰影渲染效果已經(jīng)很不錯(cuò),但當(dāng)我們?cè)陉幱安糠址糯螅覀冞€是可以明顯地看到陰影邊緣呈現(xiàn)鋸齒塊狀。這是由于深度圖固定的分辨率造成的。我們可以通過(guò)提高深度圖的分辨率或盡可能拉近光源截錐與場(chǎng)景的距離來(lái)減少鋸齒塊狀陰影。


    邊緣鋸齒塊狀陰影
  • 針對(duì)陰影的鋸齒邊緣還有另外一種解決方法稱(chēng)為百分比漸進(jìn)過(guò)濾(percentage-closer filtering, PCF)。PCF是一個(gè)術(shù)語(yǔ),它包含了多種產(chǎn)生柔和陰影的過(guò)濾方法。PCF的主要思想就是對(duì)深度圖進(jìn)行不止一次的采樣,每次使用稍微不同的坐標(biāo)值,然后結(jié)合所有采樣結(jié)果取均值。
  • PCF一種簡(jiǎn)單的實(shí)現(xiàn)就是對(duì)深度圖紋理元四周進(jìn)行采樣取均值。
float shadow = 0.0;
vec2 texelSize = 1.0 / textureSize(shadowMap, 0);
for (int x = -1; x <= 1; ++x)
{
    for (int y = -1; y <= 1; ++y)
    {
        float pcfDepth = texture(shadowMap, prokCoord.xy + vec2(x, y) * texelSize).r;
        shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0; 
    }
}
shadow /= 9.0;
1. textureSize函數(shù)返回指定紋理在mipmap第一層的寬和高。
  • 渲染效果。


    PCF陰影效果
  • 不過(guò)對(duì)陰影放大拉近,還是可以看到由于分辨率導(dǎo)致的偽影。


    PCF陰影偽影

5. 正射投影vs透視投影

  • 使用正射或透視投影矩陣這兩種投影方法渲染深度圖,陰影渲染效果存在差異。正射投影不會(huì)通過(guò)視角改變場(chǎng)景,因此視線/光線都是平行的。透視投影基于視角改變了所有的頂點(diǎn),因此產(chǎn)生了不同的陰影效果。見(jiàn)下圖:(圖片取自書(shū)中
    深度圖的正射和透視投影對(duì)比
  • 一般正射投影用于定向光源,透視投影用于聚光燈或點(diǎn)光源。
  • 透視投影與正射投影的另外一個(gè)細(xì)微差別是透視投影的深度圖經(jīng)常是大部分都是白色。這是因?yàn)橥敢曂队皩?duì)深度進(jìn)行非線性轉(zhuǎn)換,致使大部分都處于近片面附近。要想像正射投影那樣查看深度圖,我們需要像在深度測(cè)試章節(jié)那樣將非線性深度值轉(zhuǎn)換為線性。
#version 330 core
out vec4 FragColor;

in vec2 TexCoords;

uniform sampler2D depthMap;
uniform float near_plane;
uniform float far_plane;

float LinearizeDepth(float depth)
{
    float z = depth * 2.0 - 1.0;   // 轉(zhuǎn)換到NDC
    return (2.0 * near_plane * far_plane) / (far_plane + near_plane - z * (far_plane - near_plane));
}

void main()
{
    float depthValue = texture(depthMap, TexCoords).r;
    FragColor = vec4(vec3(LinearizeDepth(depthValue) / far_plane), 1.0);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容