Face culling acting strange

Started by Andrew_3ds, February 27, 2016, 06:17:59

Previous topic - Next topic

Andrew_3ds

I have no idea why this has been happening. I understand OpenGL well and have been using it for a while but even the simplest thing is baffling me. For some reason, when I set face culling the use GL_CCW, nothing renders at all. I set the square to rotate so I should at least be able to see the back of it when it rotates 180 degrees, but nothing is appearing. The weird thing is, when culling is set to GL_CW, the faces appear, however the front is being culled not the back, as it should be. For some reason, clockwise culling is working as it should, but counter clockwise culling is culling everything. I'm simply trying to draw a quad for testing performance on something, but when I try to use culling this is happening. I haven't worked with culling much because it just always worked for me, and I might be doing something wrong because I don't relatively use it so I might be missing something.
Here are my vertices:
Vertex[] vertices = {
    new Vertex(new Vector3f(-1.0F, 1.0F, 0.0F), new Vector2f(0,0)),
    new Vertex(new Vector3f(-1.0F,-1.0F, 0.0F), new Vector2f(0,1)),
    new Vertex(new Vector3f( 1.0F,-1.0F, 0.0F), new Vector2f(1,1)),
    new Vertex(new Vector3f( 1.0F, 1.0F, 0.0F), new Vector2f(1,0))
};

int[] indices = {
    0, 1, 2,
    2, 3, 0
};

enableGLInt(GL_CULL_FACE, true);
glCullFace(GL_BACK);
glFrontFace(GL_CCW);

Kai

Cannot reproduce. Maybe your vertex (and/or geometry) shader is doing something unusual to the vertices?

Here is a simple demo that shows that culling front and back faces with them being either clockwise or counter-clockwise winded, works:

import org.joml.Matrix4f;
import org.lwjgl.BufferUtils;
import org.lwjgl.glfw.*;

import java.nio.FloatBuffer;

import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.system.MemoryUtil.*;

public class CullingDemo {
    GLFWErrorCallback errorCallback;
    GLFWKeyCallback   keyCallback;
    GLFWFramebufferSizeCallback fbCallback;

    long window;
    int width = 600;
    int height = 600;

    Matrix4f projMatrix = new Matrix4f();
    Matrix4f viewMatrix = new Matrix4f();
    FloatBuffer fb = BufferUtils.createFloatBuffer(16);
    boolean cullTheFront;
    boolean frontIsCw;

    void run() {
        try {
            init();
            loop();
            glfwDestroyWindow(window);
            keyCallback.free();
        } finally {
            glfwTerminate();
            errorCallback.free();
        }
    }
    void init() {
        glfwSetErrorCallback(errorCallback = GLFWErrorCallback.createPrint(System.err));
        if (glfwInit() != GLFW_TRUE)
            throw new IllegalStateException("Unable to initialize GLFW");

        // Configure our window
        glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
        glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);

        window = glfwCreateWindow(width, height, "Hello Culling!", NULL, NULL);
        if (window == NULL)
            throw new RuntimeException("Failed to create the GLFW window");

        System.out.println("Press 'F' to toggle between frontface and backface culling (defaults to backface)");
        System.out.println("Press 'C' to toggle between clockwise or counter-clockwise frontface winding (defaults to counter-clockwise)");
        glfwSetKeyCallback(window, keyCallback = new GLFWKeyCallback() {
            @Override
            public void invoke(long window, int key, int scancode, int action, int mods) {
                if (key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE)
                    glfwSetWindowShouldClose(window, GLFW_TRUE);
                else if (key == GLFW_KEY_F && action == GLFW_RELEASE) {
                    cullTheFront = !cullTheFront;
                    if (cullTheFront)
                        System.out.println("Frontfaces are now culled");
                    else
                        System.out.println("Backfaces are now culled");
                } else if (key == GLFW_KEY_C && action == GLFW_RELEASE) {
                    frontIsCw = !frontIsCw;
                    if (frontIsCw)
                        System.out.println("Frontfaces use clockwise winding");
                    else
                        System.out.println("Frontfaces use counter-clockwise winding");
                }
            }
        });
        glfwSetFramebufferSizeCallback(window, fbCallback = new GLFWFramebufferSizeCallback() {
            @Override
            public void invoke(long window, int w, int h) {
                if (w > 0 && h > 0) {
                    width = w;
                    height = h;
                }
            }
        });
        glfwMakeContextCurrent(window);
        glfwShowWindow(window);
    }
    void renderCube() {
        // Render a cube with counter-clockwise front faces
        glBegin(GL_QUADS);
        glColor3f(  0.0f,  0.0f,  0.2f);
        glVertex3f( 1, -1, -1);
        glVertex3f(-1, -1, -1);
        glVertex3f(-1,  1, -1);
        glVertex3f( 1,  1, -1);
        glColor3f(  0.0f,  0.0f,  1.0f);
        glVertex3f( 1, -1,  1);
        glVertex3f( 1,  1,  1);
        glVertex3f(-1,  1,  1);
        glVertex3f(-1, -1,  1);
        glColor3f(  1.0f,  0.0f,  0.0f);
        glVertex3f( 1, -1, -1);
        glVertex3f( 1,  1, -1);
        glVertex3f( 1,  1,  1);
        glVertex3f( 1, -1,  1);
        glColor3f(  0.2f,  0.0f,  0.0f);
        glVertex3f(-1, -1,  1);
        glVertex3f(-1,  1,  1);
        glVertex3f(-1,  1, -1);
        glVertex3f(-1, -1, -1);
        glColor3f(  0.0f,  1.0f,  0.0f);
        glVertex3f( 1,  1,  1);
        glVertex3f( 1,  1, -1);
        glVertex3f(-1,  1, -1);
        glVertex3f(-1,  1,  1);
        glColor3f(  0.0f,  0.2f,  0.0f);
        glVertex3f( 1, -1, -1);
        glVertex3f( 1, -1,  1);
        glVertex3f(-1, -1,  1);
        glVertex3f(-1, -1, -1);
        glEnd();
    }
    void loop() {
        createCapabilities();

        glClearColor(0.6f, 0.7f, 0.8f, 1.0f);
        // Enable depth testing and culling
        glEnable(GL_DEPTH_TEST);
        glEnable(GL_CULL_FACE);

        long firstTime = System.nanoTime();
        while (glfwWindowShouldClose(window) == GLFW_FALSE) {
            long thisTime = System.nanoTime();
            float diff = (thisTime - firstTime) / 1E9f;
            float angle = diff;

            projMatrix.setPerspective((float) Math.toRadians(45.0f), (float) width / height, 0.01f, 100.0f).get(fb);
            glMatrixMode(GL_PROJECTION);
            glLoadMatrixf(fb);
            viewMatrix.setLookAt(0.0f, 2.0f, 5.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f).rotateY(angle * (float) Math.toRadians(45)).get(fb);
            glMatrixMode(GL_MODELVIEW);
            glLoadMatrixf(fb);

            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
            glCullFace(cullTheFront ? GL_FRONT : GL_BACK);
            glFrontFace(frontIsCw ? GL_CW : GL_CCW);
            glViewport(0, 0, width, height);
            renderCube();

            glfwSwapBuffers(window);
            glfwPollEvents();
        }
    }
    public static void main(String[] args) {
        new CullingDemo().run();
    }
}

Andrew_3ds

The demo runs just fine on my computer, so it can't be a bug in with my driver. I honestly have no idea why this is happening. When I set face culling to use GL_FRONT instead of GL_BACK, it renders the back face. It just doesn't want to render the front face for some reason. Here is my vertex shader code, however it's extremely simple so I don't really know how it could be causing any problems.
#version 330 core

layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec2 inTexCoord;

out vec2 outTexCoord;

uniform struct Matrix {
    mat4 model;
    mat4 view;
    mat4 projection;
} matrix;

void main() {
    outTexCoord = inTexCoord;
    
    vec3 pos = inPosition;
    
    gl_Position = matrix.projection * matrix.view * matrix.model * vec4(pos, 1.0);
}

Cornix

Are you checking for OpenGL errors? It might not have anything to do with the code you have shown. Sometimes if you get an error somewhere it will mess up everything else.

Andrew_3ds

Yes, I call GLUtil.checkGLError basically after ever type of buffer upload and after every frame draw. I don't think it's error related because it's only happening when I cull the back face of a counter clockwise mesh.

Andrew_3ds

I finally figured it out, it was a depth storage error in my framebuffer. It is still strange how it only affected one type of culling.