Masalah pemetaan bayangan pertama kali

9

Saya telah menerapkan pemetaan bayangan dasar untuk pertama kalinya di OpenGL menggunakan shader dan saya menghadapi beberapa masalah. Di bawah ini Anda dapat melihat contoh pemandangan yang saya buat:

masukkan deskripsi gambar di sini

Proses pemetaan bayangan yang saya ikuti adalah bahwa saya membuat adegan ke framebuffer menggunakan Matriks Tampilan dari sudut pandang cahaya dan matriks proyeksi dan model yang digunakan untuk rendering normal.

Pada pass kedua, saya mengirim matriks MVP di atas dari sudut pandang cahaya ke vertex shader yang mengubah posisi menjadi ruang terang. Shader fragmen melakukan pembagian perspektif dan mengubah posisi ke koordinat tekstur.

Ini vertex shader saya,


#version 150 core

uniform mat4 ModelViewMatrix;
uniform mat3 NormalMatrix;
uniform mat4 MVPMatrix;
uniform mat4 lightMVP;

uniform float scale;

in vec3 in_Position;
in vec3 in_Normal;
in vec2 in_TexCoord;

smooth out vec3 pass_Normal;
smooth out vec3 pass_Position;
smooth out vec2 TexCoord;
smooth out vec4 lightspace_Position;

void main(void){
    pass_Normal = NormalMatrix * in_Normal; 
    pass_Position = (ModelViewMatrix * vec4(scale * in_Position, 1.0)).xyz;
    lightspace_Position = lightMVP * vec4(scale * in_Position, 1.0);
    TexCoord = in_TexCoord;

    gl_Position = MVPMatrix * vec4(scale * in_Position, 1.0);
}

Dan shader fragmen saya,


#version 150 core

struct Light{
    vec3 direction;
};

uniform Light light;
uniform sampler2D inSampler;
uniform sampler2D inShadowMap;

smooth in vec3 pass_Normal;
smooth in vec3 pass_Position;
smooth in vec2 TexCoord;
smooth in vec4 lightspace_Position;

out vec4 out_Color;


float CalcShadowFactor(vec4 lightspace_Position){

    vec3 ProjectionCoords = lightspace_Position.xyz / lightspace_Position.w;

    vec2 UVCoords;
    UVCoords.x = 0.5 * ProjectionCoords.x + 0.5;
    UVCoords.y = 0.5 * ProjectionCoords.y + 0.5;

    float Depth = texture(inShadowMap, UVCoords).x;
    if(Depth < (ProjectionCoords.z + 0.001)) return 0.5;
    else return 1.0;
}

void main(void){

    vec3 Normal = normalize(pass_Normal);
    vec3 light_Direction = -normalize(light.direction);
    vec3 camera_Direction = normalize(-pass_Position);
    vec3 half_vector = normalize(camera_Direction + light_Direction);

    float diffuse = max(0.2, dot(Normal, light_Direction));
    vec3 temp_Color = diffuse * vec3(1.0);

    float specular = max( 0.0, dot( Normal, half_vector) );

    float shadowFactor = CalcShadowFactor(lightspace_Position);

    if(diffuse != 0 && shadowFactor > 0.5){
        float fspecular = pow(specular, 128.0);
        temp_Color += fspecular;
    }

    out_Color = vec4(shadowFactor * texture(inSampler, TexCoord).xyz * temp_Color, 1.0);
}

Salah satu masalah adalah bayangan diri seperti yang Anda lihat dalam gambar, peti memiliki bayangan sendiri. Apa yang saya coba adalah mengaktifkan offset poligon (yaitu glEnable (POLYGON_OFFSET_FILL), glPolygonOffset (GLfloat, GLfloat)) tetapi tidak banyak berubah. Seperti yang Anda lihat di fragmen shader, saya telah meletakkan nilai offset statis 0,001 tetapi saya harus mengubah nilai tergantung pada jarak cahaya untuk mendapatkan efek yang lebih diinginkan, yang tidak terlalu berguna. Saya juga mencoba menggunakan pemusnahan wajah depan ketika saya membuat framebuffer, itu tidak banyak berubah juga.

Masalah lainnya adalah bahwa piksel di luar frustum tampilan Cahaya mendapatkan naungan. Satu-satunya objek yang seharusnya bisa membuat bayangan adalah peti. Saya kira saya harus memilih proyeksi yang lebih tepat dan melihat matriks, tetapi saya tidak yakin bagaimana melakukannya. Apa sajakah praktik umum, yang harus saya pilih proyeksi ortografis?

Dari googling sekitar sedikit, saya mengerti bahwa masalah ini tidak sepele. Adakah yang punya mudah menerapkan solusi untuk masalah ini. Bisakah Anda memberi saya beberapa tips tambahan?

Tolong tanyakan kepada saya jika Anda memerlukan informasi lebih lanjut tentang kode saya.

Berikut ini adalah perbandingan dengan dan tanpa pemetaan bayangan dari close-up peti. Bayangan diri lebih terlihat.

Grieverheart
sumber
Sepertinya Anda harus menjelaskan masalah Anda lebih detail. Anda memiliki satu kalimat tentang masalah Anda dan kalimat berikutnya menyarankan perbaikan untuknya. Beritahu kami persis apa masalahnya dan apa yang Anda lakukan sudah mencoba dan memperbaikinya.
MichaelHouse
Saya mengedit posting saya, saya harap ini lebih baik sekarang.
Grieverheart

Jawaban:

6

Anda seharusnya tidak membayangi poligon yang menghadap ke belakang dari cahaya.

Saya mengubah shader fragmen Anda menjadi kode di bawah ini.

float CalcShadowFactor(vec4 lightspace_Position)
{

    vec3 ProjectionCoords = lightspace_Position.xyz / lightspace_Position.w;

    vec2 UVCoords;
    UVCoords.x = 0.5 * ProjectionCoords.x + 0.5;
    UVCoords.y = 0.5 * ProjectionCoords.y + 0.5;

    float Depth = texture(inShadowMap, UVCoords).x;
    if(Depth < (ProjectionCoords.z + 0.001)) return 0.5;
    else return 1.0;
}

void main(void)
{

    vec3 Normal = normalize(pass_Normal);
    vec3 light_Direction = -normalize(light.direction);
    vec3 camera_Direction = normalize(-pass_Position);
    vec3 half_vector = normalize(camera_Direction + light_Direction);

    float fndotl = dot(Normal, light_Direction);
    float shadowFactor = CalcShadowFactor(lightspace_Position);
    float diffuse = max(0.0, fndotl) * shadowFactor + 0.2;
    vec3 temp_Color = vec3(diffuse);

    float specular = max( 0.0, dot( Normal, half_vector) );

    if(shadowFactor > 0.9){
        float fspecular = pow(specular, 128.0);
        temp_Color += fspecular;
    }

    out_Color = vec4(shadowFactor * texture(inSampler, TexCoord).xyz * temp_Color, 1.0);
}
kochol
sumber
8

Mengenai bayangan diri, saya tidak yakin apa yang Anda maksudkan - saya tidak melihat "bayangan jerawat" pada peti di tangkapan layar Anda. Jika Anda maksudkan bahwa wajah-wajah yang menghadap jauh dari terang itu gelap, yah, tentu saja itu - mereka seharusnya! :) Wajah samping memang terlihat sedikit aneh, membelah diagonal menjadi setengah menyala dan setengah teduh. Jika Anda menggunakan N dot L untuk penerangan, dan Anda memiliki normals yang baik (yaitu normals keras pada kubus ini, bukan normals yang dihaluskan) yang seharusnya tidak terjadi.

Menggunakan offset poligon dan / atau menggambar wajah belakang ke peta bayangan adalah solusi standar (well ... workarounds really) untuk jerawat bayangan.

Sedangkan untuk matriks proyeksi, Anda harus menganggap cahaya sebagai kamera. Jika itu adalah titik cahaya, itu akan menjadi kamera perspektif dan jika itu adalah cahaya terarah itu akan seperti kamera ortografis. Buat matriks proyeksi dengan cara yang sama seperti yang Anda lakukan untuk kamera, menggunakan bidang pandang cahaya, rasio aspek, dll.

Untuk membatasi shader piksel untuk hanya menggambar piksel dalam frustum tampilan cahaya, setelah Anda menghitung bayangan peta UV hanya memeriksa apakah mereka antara 0 dan 1 dan discardjika tidak (atau mengatur warna cahaya ke nol). Cara lain adalah dengan mengatur tekstur peta bayangan ke mode "clamp to border" dan mengatur batas warna nol, yang akan memastikan segala sesuatu di luar peta bayangan mendapat bayangan penuh.

Nathan Reed
sumber
Ini adalah closeup dari kotak dan perbandingan dengan dan tanpa tautan pemetaan bayangan . Bayangan diri lebih terlihat di sini. Bisakah Anda menjelaskan mengapa Anda akan menggunakan kamera perspektif untuk lampu titik dan ortografis untuk pengarahan?
Grieverheart
Oke, dalam suntikan itu memang terlihat seperti bayangan jerawat. Menggambar wajah kembali ke peta bayangan (dan bukan wajah depan) harus memperbaikinya. Saya tahu Anda mengatakan Anda mencobanya, tetapi mungkin ada yang salah? Hanya saja glCullFace(GL_FRONT). Adapun point vs directional, itu semua tentang sinar. Sinar kamera menyatu ke suatu titik dalam perspektif kamera dan sinar cahaya menyatu ke titik dalam cahaya titik; sinar kamera sejajar untuk kamera ortografis dan sinar cahaya sejajar untuk cahaya arah.
Nathan Reed
Saya mengerti, itu masuk akal. Berikut ini adalah perbandingan lain dengan dan tanpa wajah culling. Yang di bagian bawah telah mengaktifkan culling wajah. Saya akan mencoba menggunakan proyeksi ortografis untuk matriks View light saya, yang seharusnya tidak mempengaruhi masalah bayangan diri sendiri, kan? Mungkin rentang klip saya terlalu besar (yaitu saya memiliki 0,1 hingga 100)?
Grieverheart
Itu terlihat persis sama. Anda menggunakan pemusnahan wajah belakang selama sisa adegan, kan? Apakah kamu melakukannya glEnable(GL_CULL_FACE)?
Nathan Reed
Mereka tidak persis sama, perhatikan kotak itu. Pada gambar di atas, garis bayangan cembung sedangkan yang di bawah adalah cekung.
Grieverheart
3

Pixel di luar tampilan lampu gelap karena diambil sampelnya dari luar tekstur kedalaman cahaya.

Ketika Anda mengubah titik oleh matriks proyeksi Anda mendapatkan koordinat klip [lightspace_Position] dari fragmen. Anda kemudian mengubahnya menjadi koordinat tekstur [UVCoords]. Namun, koordinat ruang klip dari fragmen masih dapat berada di luar rentang -1.0 hingga 1.0 (dari layar dan karenanya terpotong) dan karenanya koordinat tekstur yang dikonversi dapat berada di luar kisaran 0,0 hingga 1,0 dari tekstur Anda.

Coba ini:

if(UVCoords.x >= 0.0 && UVCoords.x <= 1.0 && UVCoords.y >= 0.0 && UVCoords.y <= 1.0 && (Depth < (ProjectionCoords.z + 0.001)))
    return 0.5;

Anda juga dapat melipatgandakan matriks bias ke dalam [lightMVP] Anda dan menghindari melakukan konversi koordinat dalam shader fragmen Anda.

bgrater
sumber
Matriks bias biasanya adalah cara orang pergi. Ini jauh lebih cepat daripada kode percabangan dalam shader fragmen.
Ian Young