Я пытаюсь использовать Jogl и его класс слушателей событий, чтобы сделать шейдер Shadowmap. Я бы хотел, чтобы это было 2D (сверху вниз), где вид или камера центрируется выше, глядя вниз. Затем он должен иметь точечный свет, который сталкивается со всеми 4 направлениями (x, -x, y, -y), а его z остается постоянным и установленным. Когда я рендерирует объект, объект должен препятствовать свету и иметь тень, видимую на плоскости x, y. Я использую объект VAO для создания Dexit Map, в моих шейдерах 1-й проход, затем во втором я создаю тени, создаю рассеянный градиент с точечного света. < /P>
Вот мой код: < /p>
import com.jogamp.opengl.*;
import com.sun.prism.impl.BufferUtil;
import org.joml.Matrix4f;
import org.joml.Vector3f;
import java.io.IOException;
import java.nio.FloatBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import static com.jogamp.opengl.GL2.*;
public class Event implements GLEventListener {
private int shaderProgram;
private int shadowFBO, shadowTexture;
private int vaoObject, vaoGround; // One for the occluder and one for the ground
// Uniform locations
private int modelLoc, viewLoc, projLoc, lightSpaceLoc, lightPosLoc, passModeLoc, shadowMapLoc;
// Matrices
private Matrix4f modelMatrix = new Matrix4f();
private Matrix4f viewMatrix = new Matrix4f();
private Matrix4f projectionMatrix = new Matrix4f();
private Matrix4f lightSpaceMatrix = new Matrix4f();
// Light and camera positions (world space)
private Vector3f lightPos = new Vector3f(2.0f, 2.0f, 0.0f); // Light on the XY plane
private Vector3f cameraPos = new Vector3f(0.0f, 0.0f, 10.0f); // Top‑down camera
// Shadow map settings
private int shadowMapWidth = 1024, shadowMapHeight = 1024;
// shadowRange determines how far the light covers (used for normalization)
private float shadowRange = 10.0f;
@Override
public void init(GLAutoDrawable drawable) {
GL2 gl = drawable.getGL().getGL2();
// Set clear color to white and enable depth testing.
gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
gl.glEnable(GL_DEPTH_TEST);
// Compile and link the shader program.
shaderProgram = createShaderProgram(gl, "res/shadowVertexShader.glsl", "res/shadowFragmentShader.glsl");
// Bind the sampler uniform "shadowMap" to texture unit 0.
gl.glUseProgram(shaderProgram);
gl.glUniform1i(shadowMapLoc, 0); // Bind to texture unit 0
gl.glUseProgram(0);
// Retrieve other uniform locations.
gl.glUseProgram(shaderProgram);
modelLoc = gl.glGetUniformLocation(shaderProgram, "model");
viewLoc = gl.glGetUniformLocation(shaderProgram, "view");
projLoc = gl.glGetUniformLocation(shaderProgram, "projection");
lightSpaceLoc = gl.glGetUniformLocation(shaderProgram, "lightSpace");
lightPosLoc = gl.glGetUniformLocation(shaderProgram, "lightPos");
passModeLoc = gl.glGetUniformLocation(shaderProgram, "passMode");
gl.glUseProgram(0);
// Set up geometry:
// - The occluding object (a quad from -1 to 1)
vaoObject = setupObjectVAO(gl);
// - The ground (a larger quad covering the scene)
vaoGround = setupGroundVAO(gl);
// --- Create Shadow Map FBO and Depth Texture ---
int[] fbos = new int[1];
gl.glGenFramebuffers(1, fbos, 0);
shadowFBO = fbos[0];
int[] textures = new int[1];
gl.glGenTextures(1, textures, 0);
shadowTexture = textures[0];
gl.glBindTexture(GL_TEXTURE_2D, shadowTexture);
// Create a depth texture to store normalized depth values.
gl.glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, shadowMapWidth, shadowMapHeight,
0, GL_DEPTH_COMPONENT, GL_FLOAT, null);
gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
float[] borderColor = {1, 1, 1, 1};
gl.glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor, 0);
// IMPORTANT: Disable hardware depth comparison to sample raw depth values.
gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
gl.glBindFramebuffer(GL_FRAMEBUFFER, shadowFBO);
gl.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, shadowTexture, 0);
gl.glDrawBuffer(GL_NONE);
gl.glReadBuffer(GL_NONE);
if (gl.glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
System.err.println("Error: Shadow FBO not complete.");
}
gl.glBindFramebuffer(GL_FRAMEBUFFER, 0);
gl.glEnable(gl.GL_DEBUG_OUTPUT);
}
@Override
public void display(GLAutoDrawable drawable) {
GL2 gl = drawable.getGL().getGL2();
// --- Shadow Map Pass ---
// Set up the light's view and projection.
Matrix4f lightView = new Matrix4f().lookAt(lightPos, new Vector3f(0, 0, 0), new Vector3f(0, 0, 1));
Matrix4f lightProj = new Matrix4f().ortho(-shadowRange, shadowRange, -shadowRange, shadowRange, 1.0f, 20.0f);
lightSpaceMatrix = new Matrix4f();
lightProj.mul(lightView, lightSpaceMatrix);
// Render the occluding object into the shadow map.
gl.glViewport(0, 0, shadowMapWidth, shadowMapHeight);
gl.glBindFramebuffer(GL_FRAMEBUFFER, shadowFBO);
gl.glClear(GL_DEPTH_BUFFER_BIT);
gl.glUseProgram(shaderProgram);
gl.glUniform1i(passModeLoc, 0); // passMode 0: shadow map generation
modelMatrix.identity(); // Object centered at origin.
gl.glUniformMatrix4fv(modelLoc, 1, false, toFloatBuffer(modelMatrix));
gl.glUniformMatrix4fv(viewLoc, 1, false, toFloatBuffer(lightView));
gl.glUniformMatrix4fv(projLoc, 1, false, toFloatBuffer(lightProj));
gl.glUniformMatrix4fv(lightSpaceLoc, 1, false, toFloatBuffer(lightSpaceMatrix));
gl.glUniform3f(lightPosLoc, lightPos.x, lightPos.y, lightPos.z);
renderObject(gl, vaoObject);
gl.glBindFramebuffer(GL_FRAMEBUFFER, 0);
// --- Scene Pass ---
int width = drawable.getSurfaceWidth();
int height = drawable.getSurfaceHeight();
gl.glViewport(0, 0, width, height);
gl.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Set up a top‑down camera view.
viewMatrix.identity().lookAt(cameraPos, new Vector3f(0, 0, 0), new Vector3f(0, 1, 0));
projectionMatrix.identity().ortho(-5, 5, -5, 5, 1.0f, 20.0f);
gl.glUniform1i(passModeLoc, 1); // passMode 1: scene shading
gl.glUniformMatrix4fv(viewLoc, 1, false, toFloatBuffer(viewMatrix));
gl.glUniformMatrix4fv(projLoc, 1, false, toFloatBuffer(projectionMatrix));
gl.glUniformMatrix4fv(lightSpaceLoc, 1, false, toFloatBuffer(lightSpaceMatrix));
gl.glUniform3f(lightPosLoc, lightPos.x, lightPos.y, lightPos.z);
// Bind the shadow map texture for sampling.
gl.glActiveTexture(GL_TEXTURE0);
gl.glBindTexture(GL_TEXTURE_2D, shadowTexture);
// Render the ground first.
modelMatrix.identity(); // Ground at z = 0.
gl.glUniformMatrix4fv(modelLoc, 1, false, toFloatBuffer(modelMatrix));
renderObject(gl, vaoGround);
// Then render the occluding object on top with a slight z-offset.
modelMatrix.identity().translate(0, 0, 0.01f);
gl.glUniformMatrix4fv(modelLoc, 1, false, toFloatBuffer(modelMatrix));
renderObject(gl, vaoObject);
gl.glUseProgram(0);
System.out.println(gl.glGetError());
}
// Helper: Converts a Matrix4f to a FloatBuffer.
private FloatBuffer toFloatBuffer(Matrix4f mat) {
FloatBuffer fb = BufferUtil.newFloatBuffer(16);
mat.get(fb);
return fb;
}
// Sets up a VAO for the occluding object (a quad from -1 to 1).
private int setupObjectVAO(GL2 gl) {
float[] vertices = {
// positions // tex coords (unused, but passed through)
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
};
int[] vao = new int[1];
int[] vbo = new int[1];
gl.glGenVertexArrays(1, vao, 0);
gl.glGenBuffers(1, vbo, 0);
gl.glBindVertexArray(vao[0]);
gl.glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
FloatBuffer vertexBuffer = BufferUtil.newFloatBuffer(vertices.length);
vertexBuffer.put(vertices).flip();
gl.glBufferData(GL_ARRAY_BUFFER, vertices.length * Float.BYTES, vertexBuffer, GL_STATIC_DRAW);
// Position attribute (location 0)
gl.glVertexAttribPointer(0, 3, GL_FLOAT, false, 5 * Float.BYTES, 0);
gl.glEnableVertexAttribArray(0);
// Tex coord attribute (location 1)
gl.glVertexAttribPointer(1, 2, GL_FLOAT, false, 5 * Float.BYTES, 3 * Float.BYTES);
gl.glEnableVertexAttribArray(1);
gl.glBindBuffer(GL_ARRAY_BUFFER, 0);
gl.glBindVertexArray(0);
return vao[0];
}
// Sets up a VAO for the ground (a large quad covering -5 to 5).
private int setupGroundVAO(GL2 gl) {
float[] vertices = {
// positions // tex coords
-5.0f, -5.0f, 0.0f, 0.0f, 0.0f,
5.0f, -5.0f, 0.0f, 1.0f, 0.0f,
-5.0f, 5.0f, 0.0f, 0.0f, 1.0f,
5.0f, 5.0f, 0.0f, 1.0f, 1.0f,
};
int[] vao = new int[1];
int[] vbo = new int[1];
gl.glGenVertexArrays(1, vao, 0);
gl.glGenBuffers(1, vbo, 0);
gl.glBindVertexArray(vao[0]);
gl.glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
FloatBuffer vertexBuffer = BufferUtil.newFloatBuffer(vertices.length);
vertexBuffer.put(vertices).flip();
gl.glBufferData(GL_ARRAY_BUFFER, vertices.length * Float.BYTES, vertexBuffer, GL_STATIC_DRAW);
gl.glVertexAttribPointer(0, 3, GL_FLOAT, false, 5 * Float.BYTES, 0);
gl.glEnableVertexAttribArray(0);
gl.glVertexAttribPointer(1, 2, GL_FLOAT, false, 5 * Float.BYTES, 3 * Float.BYTES);
gl.glEnableVertexAttribArray(1);
gl.glBindBuffer(GL_ARRAY_BUFFER, 0);
gl.glBindVertexArray(0);
return vao[0];
}
private void renderObject(GL2 gl, int vao) {
gl.glBindVertexArray(vao);
gl.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
gl.glBindVertexArray(0);
}
// Loads, compiles, and links the vertex and fragment shaders.
private int createShaderProgram(GL2 gl, String vertPath, String fragPath) {
String vertSource = "";
String fragSource = "";
try {
vertSource = new String(Files.readAllBytes(Paths.get(vertPath)), StandardCharsets.UTF_8);
fragSource = new String(Files.readAllBytes(Paths.get(fragPath)), StandardCharsets.UTF_8);
} catch (IOException e) {
System.err.println("Error loading shader files: " + e.getMessage());
}
int vertexShader = gl.glCreateShader(GL2.GL_VERTEX_SHADER);
gl.glShaderSource(vertexShader, 1, new String[]{vertSource}, null, 0);
gl.glCompileShader(vertexShader);
int[] compiled = new int[1];
gl.glGetShaderiv(vertexShader, GL2.GL_COMPILE_STATUS, compiled, 0);
if(compiled[0] == GL_FALSE) {
System.err.println("Vertex shader compilation failed.");
}
int fragmentShader = gl.glCreateShader(GL2.GL_FRAGMENT_SHADER);
gl.glShaderSource(fragmentShader, 1, new String[]{fragSource}, null, 0);
gl.glCompileShader(fragmentShader);
gl.glGetShaderiv(fragmentShader, GL2.GL_COMPILE_STATUS, compiled, 0);
if(compiled[0] == GL_FALSE) {
System.err.println("Fragment shader compilation failed.");
}
int program = gl.glCreateProgram();
gl.glAttachShader(program, vertexShader);
gl.glAttachShader(program, fragmentShader);
gl.glLinkProgram(program);
int[] linked = new int[1];
gl.glGetProgramiv(program, GL2.GL_LINK_STATUS, linked, 0);
if(linked[0] == GL_FALSE) {
System.err.println("Shader program linking failed.");
}
return program;
}
@Override
public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
GL2 gl = drawable.getGL().getGL2();
gl.glViewport(0, 0, width, height);
}
@Override
public void dispose(GLAutoDrawable drawable) {
// Clean up resources (FBOs, textures, shader program, VAOs) as needed.
}
}
< /code>
А вот мои коды шейдеров, начиная с вершины < /p>
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform mat4 lightSpace;
out vec4 FragPosLight; // For shadow lookup in the fragment shader
out vec3 FragPos; // World-space position
out vec2 TexCoords; // Pass texture coordinates to the fragment shader
void main() {
vec4 worldPos = model * vec4(aPos, 1.0);
FragPos = worldPos.xyz;
FragPosLight = lightSpace * worldPos;
TexCoords = aTexCoord; // Forward the texture coordinate
gl_Position = projection * view * worldPos;
}
< /code>
и фрагмент < /p>
#version 330 core
in vec4 FragPosLight;
in vec3 FragPos;
in vec2 TexCoords; // Ensure your vertex shader outputs this if needed
uniform sampler2D shadowMap;
uniform sampler2D sceneTexture; // Only if using post-processing (passMode 2)
uniform vec3 lightPos;
uniform int passMode; // 0 = shadow map generation, 1 = scene shading, 2 = post-processing
out vec4 FragColor;
void main() {
if(passMode == 0) {
// Shadow Map Generation Pass
float maxDist = 10.0;
float xyDist = length(FragPos.xy - lightPos.xy);
gl_FragDepth = clamp(xyDist / maxDist, 0.0, 1.0);
return;
}
else if(passMode == 1) {
// Scene Shading Pass
float maxDist = 10.0;
// Compute shadow texture coordinates (example: adjust as needed)
vec2 shadowUV = (FragPos.xy - (lightPos.xy - vec2(maxDist))) / (2.0 * maxDist);
float currentDepth = length(FragPos.xy - lightPos.xy) / maxDist;
float shadowMapDepth = texture(shadowMap, shadowUV).r;
float bias = 0.01;
float shadow = (currentDepth > shadowMapDepth + bias) ? 1.0 : 0.0;
vec3 lightColor = vec3(1.0);
// For debugging: output red in shadowed areas
FragColor = vec4((1.0 - shadow) * lightColor, 1.0);
}
else if(passMode == 2) {
// Post‑Processing Pass (if needed)
FragColor = texture(sceneTexture, TexCoords);
}
}
< /code>
Я думаю, что значения глубины моего VAO могут вызывать проблемы, а также перевод системы координат, которую я использую для кода JOGL (создание VAOS) и шейдера GLSL, которая использует систему координат 0 → 1 для X и 0 → 1 для Y. Также отдельная проблема может привести к неверной ошибке работы. Мой результат отображается как белый экран.
Подробнее здесь: https://stackoverflow.com/questions/794 ... ng-in-jogl