pythonopenglglslpygletshadow-mapping

How to make Shadow Mapping?


I was trying to understand how Shadow Mapping works on its own and describes this Python program to test Shadow Mapping, and this is as far as possible.

enter image description here

I searched for shadow mapping problems, but I haven't found what has already happened for something like this, so how do I color the normal rotating cube in front of the light and how do I correct the position of the shadow so that it appears and place it exactly on the surface ? I was just modifying the shader.

import pyglet, math, pyrr, ctypes
import numpy as np
from OpenGL.GL import *
from OpenGL.GL.shaders import *

app = pyglet.window.Window()

v = """
in layout(location=0) vec3 posicao;
uniform mat4 view;
uniform vec3 translate;
uniform float rot;
uniform float rot2;
uniform vec3 t2;
out vec3 outpos;
void main(){
    vec3 p = posicao;
    p = vec3(sin(rot)*p.x+cos(rot)*p.z,p.y,-sin(rot)*p.z+cos(rot)*p.x);
    p = translate+p+t2;
    p = vec3(p.x,sin(rot2)*p.z+cos(rot2)*p.y,-sin(rot2)*p.y+cos(rot2)*p.z);
    outpos = p;
    gl_Position = view*vec4(p,1);
}
"""

f = """
uniform vec3 cor;
uniform int modo;
uniform sampler2D shadow;
in vec3 outpos;
void main(){
    if(modo==0){
        float pi = 3.141592653589793;
        vec3 o = outpos+vec3(0,-5,0);
        o = vec3(o.x,sin(-30*pi/180)*o.z+cos(-30*pi/180)*o.y,-sin(-30*pi/180)*o.y+cos(-30*pi/180)*o.z);
        o.z+=0.05;
        float d = texture(shadow,vec2(.5)+o.xy/20).z;
        float i = 1;
        if(d<-o.z/20){
            i = 0.5;
        }
        gl_FragColor = vec4(vec3(cor),1)*i;
        //gl_FragColor = texture(shadow,outpos.xy);
    }else{
        gl_FragColor = vec4(vec3(-outpos.z/20),1);
    }
}
"""

shader = compileProgram(compileShader(v,GL_VERTEX_SHADER),compileShader(f,GL_FRAGMENT_SHADER))

glUseProgram(shader)

tudo = [-200,-4,200,
        200,-4,200,
        200,-4,-200,
        -200,-4,-200,
        -2,-2,2,
        -2,-2,-2,
        2,-2,-2,
        2,-2,2,
        2,2,2,
        2,2,-2,
        -2,2,-2,
        -2,2,2,
        -2,2,2,
        -2,2,-2,
        -2,-2,-2,
        -2,-2,2,
        2,-2,2,
        2,-2,-2,
        2,2,-2,
        2,2,2,
        -2,2,-2,
        2,2,-2,
        2,-2,-2,
        -2,-2,-2,
        -2,-2,2,
        2,-2,2,
        2,2,2,
        -2,2,2]

tudo = np.array(tudo, dtype=np.float32)

CUBO = glGenBuffers(1)

glBindBuffer(GL_ARRAY_BUFFER, CUBO)
glBufferData(GL_ARRAY_BUFFER, len(tudo)*4, tudo, GL_STREAM_DRAW)

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 12, ctypes.c_void_p(0))
glEnableVertexAttribArray(0)

view = pyrr.matrix44.create_perspective_projection_matrix(60, app.width/app.height, .1, 10000)
p = glGetUniformLocation(shader, "view")
glUniformMatrix4fv(p, 1, GL_FALSE, view)

glEnable(GL_DEPTH_TEST)

# Tentativa de Shadow Mapping

glEnable(GL_TEXTURE_2D)

tf = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, tf)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, app.width, app.height, 0, GL_RGB, GL_UNSIGNED_BYTE, None)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
f = glGenFramebuffers(1)
r = glGenRenderbuffers(1)
glBindFramebuffer(GL_FRAMEBUFFER, f)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tf, 0)
glBindRenderbuffer(GL_RENDERBUFFER, r)
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, app.width, app.height)
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, r)
glBindRenderbuffer(GL_RENDERBUFFER, 0)
glBindFramebuffer(GL_FRAMEBUFFER, 0)

pos = [0,-10,0]
comando = {"a":0,"w":0,"s":0,"d":0,"q":0,"e":0}
girar = -1

@app.event
def on_draw():
    global CUBO, shader, pos, comando, tudo, girar, f
    glBindFramebuffer(GL_FRAMEBUFFER, f)
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    # Z-Buffer
    p = glGetUniformLocation(shader, "modo")
    glUniform1i(p, 1)
    p = glGetUniformLocation(shader, "rot2")
    glUniform1f(p, -30*math.pi/180)
    p = glGetUniformLocation(shader, "t2")
    glUniform3f(p, 0, -5, 0)
    # Movimento
    v = .04
    if comando["a"] == 1:
        pos[0]-=v
    if comando["d"] == 1:
        pos[0]+=v
    if comando["w"] == 1:
        pos[1]-=v
    if comando["s"] == 1:
        pos[1]+=v
    if comando["q"] == 1:
        pos[2]-=v
    if comando["e"] == 1:
        pos[2]+=v
    # fundo vermelho
    p = glGetUniformLocation(shader, "translate")
    glUniform3f(p, 0, 0, 0)
    p = glGetUniformLocation(shader, "rot")
    glUniform1f(p, 0)
    p = glGetUniformLocation(shader, "cor")
    glUniform3f(p, 1, 0, 0)
    glDrawArrays(GL_QUADS, 0, 4)
    # objeto no meio
    p = glGetUniformLocation(shader, "translate")
    glUniform3f(p, -1, -5, -10)
    p = glGetUniformLocation(shader, "rot")
    glUniform1f(p, 0)
    p = glGetUniformLocation(shader, "cor")
    glUniform3f(p, 0, 0, 1)
    glDrawArrays(GL_QUADS, 8, 4)
    # objeto azul
    p = glGetUniformLocation(shader, "translate")
    glUniform3f(p, pos[0], pos[2], pos[1])
    p = glGetUniformLocation(shader, "rot")
    girar+=.4
    glUniform1f(p, girar*math.pi/180)
    p = glGetUniformLocation(shader, "cor")
    glUniform3f(p, 0, 0, 1)
    glDrawArrays(GL_QUADS, 4, 8)
    p = glGetUniformLocation(shader, "cor")
    glUniform3f(p, 0, 1, 0)
    glDrawArrays(GL_QUADS, 12, 8)
    p = glGetUniformLocation(shader, "cor")
    glUniform3f(p, 1, 1, 0)
    glDrawArrays(GL_QUADS, 20, 8)
    # gravar o Framebuffer
    glBindFramebuffer(GL_FRAMEBUFFER, 0)
    # reset
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    # Z-Buffer tirando
    p = glGetUniformLocation(shader, "modo")
    glUniform1i(p, 0)
    p = glGetUniformLocation(shader, "rot2")
    glUniform1f(p, 0*math.pi/180)
    p = glGetUniformLocation(shader, "t2")
    glUniform3f(p, 0, 0, 0)
    # fundo vermelho
    p = glGetUniformLocation(shader, "translate")
    glUniform3f(p, 0, 0, 0)
    p = glGetUniformLocation(shader, "rot")
    glUniform1f(p, 0)
    p = glGetUniformLocation(shader, "cor")
    glUniform3f(p, 1, 0, 0)
    glDrawArrays(GL_QUADS, 0, 4)
    # objeto no meio
    p = glGetUniformLocation(shader, "translate")
    glUniform3f(p, -1, -5, -10)
    p = glGetUniformLocation(shader, "rot")
    glUniform1f(p, 0)
    p = glGetUniformLocation(shader, "cor")
    glUniform3f(p, 0, 0, 1)
    glDrawArrays(GL_QUADS, 8, 4)
    # objeto azul
    p = glGetUniformLocation(shader, "translate")
    glUniform3f(p, pos[0], pos[2], pos[1])
    p = glGetUniformLocation(shader, "rot")
    glUniform1f(p, girar*math.pi/180)
    p = glGetUniformLocation(shader, "cor")
    glUniform3f(p, 0, 0, 1)
    glDrawArrays(GL_QUADS, 4, 8)
    p = glGetUniformLocation(shader, "cor")
    glUniform3f(p, 0, 1, 0)
    glDrawArrays(GL_QUADS, 12, 8)
    p = glGetUniformLocation(shader, "cor")
    glUniform3f(p, 1, 1, 0)
    glDrawArrays(GL_QUADS, 20, 8)

@app.event
def on_key_press(k,m):
    global comando, grandeG
    if k == pyglet.window.key.A:
        comando["a"] = 1
    if k == pyglet.window.key.W:
        comando["w"] = 1
    if k == pyglet.window.key.S:
        comando["s"] = 1
    if k == pyglet.window.key.D:
        comando["d"] = 1
    if k == pyglet.window.key.Q:
        comando["q"] = 1
    if k == pyglet.window.key.E:
        comando["e"] = 1

@app.event
def on_key_release(k,m):
    global comando
    if k == pyglet.window.key.A:
        comando["a"] = 0
    if k == pyglet.window.key.W:
        comando["w"] = 0
    if k == pyglet.window.key.S:
        comando["s"] = 0
    if k == pyglet.window.key.D:
        comando["d"] = 0
    if k == pyglet.window.key.Q:
        comando["q"] = 0
    if k == pyglet.window.key.E:
        comando["e"] = 0

def SRO(dt):
    on_draw()

pyglet.clock.schedule(SRO)

pyglet.app.run()

Solution

  • The issue is caused by the limited accuracy of the shadow depth buffer. Since only 1 channel per fragment needs to be stored in the shadow buffer, I recommend to use the floating point internal texture format GL_R16F (or GL_R32F):

    glTexImage2D(GL_TEXTURE_2D, 0, GL_R16F, app.width, app.height, 0, GL_RED, GL_UNSIGNED_BYTE, None)
    

    Note the (shadow) depth is now stored in the red color channel, thus you have to read the value from the red (x) channel rather than the blue (z) channel in the fragment shader:

    floaz d = texture(shadow,vec2(.5)+o.xy/20).z;

    float d = texture(shadow,vec2(.5)+o.xy/20).x;