0
\$\begingroup\$

I am trying to do make a 3D game in modern OpenGL where the player throws a ball and when it hits a cube, the score will be increased by one. I created a 3D object and camera. Right now I'm stuck because I want to draw a crosshair and HUD. I know that I need to do it in 2D space (textures should be stuck on screen) but when I render a 2D object, I can't move my camera and I only see a 2D rectangle. Source code looks like this:

#include <GL/glew.h>
#include <GLFW/glfw3.h>

#include <glm.hpp>
#include <gtc/matrix_transform.hpp>
#include <gtc/type_ptr.hpp>

#include <iostream>
#include <fstream>
#include <string>

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);

float deltaTime = 0.0f;
float lastFrame = 0.0f;
float pitch;
float yaw;
float lastX = 600, lastY = 360;
float fov = 45.0f;

bool firstMouse = false;

static unsigned int CompileShader(unsigned int type, std::string source)
{
    unsigned int id = glCreateShader(type);
    const char* src = source.c_str();
    glShaderSource(id, 1, &src, nullptr);
    glCompileShader(id);

    int result;
    glGetShaderiv(id, GL_COMPILE_STATUS, &result);
    if (result == GL_FALSE)
    {
        int length;
        glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
        char* message = (char*)_malloca(length * sizeof(char));
        glGetShaderInfoLog(id, length, &length, message);
        std::cout << "Failed to compile " << (type == GL_VERTEX_SHADER ? "vertex" : "fragment") << "shader" << std::endl;
        std::cout << message << std::endl;
        glDeleteShader(id);

        return 0;

    }

    return id;
}

static unsigned int CreateShader(const std::string& vertexShader, const std::string fragmentShader)
{
    unsigned int program = glCreateProgram();
    unsigned int vs = CompileShader(GL_VERTEX_SHADER, vertexShader);
    unsigned int fs = CompileShader(GL_FRAGMENT_SHADER, fragmentShader);

    glAttachShader(program, vs);
    glAttachShader(program, fs);
    glLinkProgram(program);

    glDeleteShader(vs);
    glDeleteShader(fs);

    return program;
}

std::string readFromFile(const char* pathToFile)
{
    std::string content;
    std::ifstream fileStream(pathToFile, std::ios::in);

    if (!fileStream.is_open()) {
        std::cerr << "Could not read file " << pathToFile << ". File does not exist." << std::endl;
        return "";
    }

    std::string line;
    while (std::getline(fileStream, line)) {
        content.append(line + "\n");
    }

    fileStream.close();
    std::cout << content << std::endl;
    return content;
}

void processInput(GLFWwindow* window)
{

    float currentFrame = glfwGetTime();
    deltaTime = currentFrame - lastFrame;
    lastFrame = currentFrame;

    float cameraSpeed = 2.5f * deltaTime; // dopasuj do swoich potrzeb  
    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
        cameraPos += cameraSpeed * cameraFront;
    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
        cameraPos -= cameraSpeed * cameraFront;
    if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
        cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
        cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
}

void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{


    if (firstMouse) // ta zmienna bool jest początkowo ustawiona na true 
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }

    GLfloat xoffset = xpos - lastX;
    GLfloat yoffset = lastY - ypos;
    lastX = xpos;
    lastY = ypos;

    GLfloat sensitivity = 0.05;
    xoffset *= sensitivity;
    yoffset *= sensitivity;

    yaw += xoffset;
    pitch += yoffset;

    if (pitch > 89.0f)
        pitch = 89.0f;
    if (pitch < -89.0f)
        pitch = -89.0f;

    glm::vec3 front;
    front.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
    front.y = sin(glm::radians(pitch));
    front.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
    cameraFront = glm::normalize(front);

}

void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
    if (fov >= 1.0f && fov <= 45.0f)
        fov -= yoffset;
    if (fov <= 1.0f)
        fov = 1.0f;
    if (fov >= 45.0f)
        fov = 45.0f;
}

int main(void)
{
    GLFWwindow* window;

    /* Initialize the library */
    if (!glfwInit())
        return -1;

    /* Create a windowed mode window and its OpenGL context */
    window = glfwCreateWindow(1200, 720, "Shoot The Cube!", NULL, NULL);
    if (!window)
    {
        glfwTerminate();
        return -1;
    }

    /* Make the window's context current */
    glfwMakeContextCurrent(window);

    if (glewInit() != GLEW_OK)
        std::cout << "Error" << std::endl;

    std::cout << glGetString(GL_VERSION) << std::endl;
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
    glfwSetCursorPosCallback(window, mouse_callback);

    float vertices[] = {
        -0.5f, -0.5f, -0.5f,    0.0f, 0.0f,
         0.5f, -0.5f, -0.5f,    1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,    1.0f, 1.0f,
         0.5f,  0.5f, -0.5f,    1.0f, 1.0f,
        -0.5f,  0.5f, -0.5f,    0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,    0.0f, 0.0f,

        -0.5f, -0.5f,  0.5f,    0.0f, 0.0f,
         0.5f, -0.5f,  0.5f,    1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,    1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,    1.0f, 1.0f,
        -0.5f,  0.5f,  0.5f,    0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,    0.0f, 0.0f,

        -0.5f,  0.5f,  0.5f,    1.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,    1.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,    0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,    0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,    0.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,    1.0f, 0.0f,

         0.5f,  0.5f,  0.5f,    1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,    1.0f, 1.0f,
         0.5f, -0.5f, -0.5f,    0.0f, 1.0f,
         0.5f, -0.5f, -0.5f,    0.0f, 1.0f,
         0.5f, -0.5f,  0.5f,    0.0f, 0.0f,
         0.5f,  0.5f,  0.5f,    1.0f, 0.0f,

        -0.5f, -0.5f, -0.5f,    0.0f, 1.0f,
         0.5f, -0.5f, -0.5f,    1.0f, 1.0f,
         0.5f, -0.5f,  0.5f,    1.0f, 0.0f,
         0.5f, -0.5f,  0.5f,    1.0f, 0.0f,
        -0.5f, -0.5f,  0.5f,    0.0f, 0.0f,
        -0.5f, -0.5f, -0.5f,    0.0f, 1.0f,

        -0.5f,  0.5f, -0.5f,    0.0f, 1.0f,
         0.5f,  0.5f, -0.5f,    1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,    1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,    1.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,    0.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,    0.0f, 1.0f
    };

    unsigned int VBO;
    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);

    float hudVertices[] = {
        -1.0f,  1.0f, 0.0f,
        -1.0f, -1.0f, 0.0f,
         1.0f, 1.0f, 0.0f,
         1.0f,  1.0f, 0.0f,
         1.0f, -1.0f, 0.0f,
        -1.0f, -1.0f, 0.0f
    };

    unsigned int CVB;
    glGenBuffers(1, &CVB);
    glBindBuffer(GL_ARRAY_BUFFER, CVB);
    glBufferData(GL_ARRAY_BUFFER, sizeof(hudVertices), hudVertices, GL_STATIC_DRAW);
    glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(2);

    std::string vertexShaderSource = readFromFile("vertexShader.shader");
    std::string fragmentShaderSource = readFromFile("fragmentShader.shader");
    std::string hudVertex = readFromFile("hudVertex.shader");
    std::string hudFragment = readFromFile("hudFragment.shader");
    unsigned int shaderProgram = CreateShader(vertexShaderSource, fragmentShaderSource);
    unsigned int hudShader = CreateShader(hudVertex, hudFragment);
    glUseProgram(shaderProgram);
    glUseProgram(hudShader);

    unsigned int bodyTex;
    glGenTextures(1, &bodyTex);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, bodyTex);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    stbi_set_flip_vertically_on_load(true);
    int width, height, nrChannels;
    unsigned char* data = stbi_load("C:/Users/Antoni/Desktop/container.jpg", &width, &height, &nrChannels, 0);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
    glGenerateMipmap(GL_TEXTURE_2D);
    stbi_image_free(data);

    unsigned int faceTex;
    glGenTextures(1, &faceTex);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, faceTex);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    data = stbi_load("C:/Users/Antoni/Desktop/face.png", &width, &height, &nrChannels, 0);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
    glGenerateMipmap(GL_TEXTURE_2D);
    stbi_image_free(data);

    glUniform1i(glGetUniformLocation(shaderProgram, "playerBody"), 0);
    glUniform1i(glGetUniformLocation(shaderProgram, "playerFace"), 1);

    glEnable(GL_DEPTH_TEST);

    /* Loop until the user closes the window */
    while (!glfwWindowShouldClose(window))
    {
        /* Render here */
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glClearColor(0.2, 0.5, 1.0, 0.5);
        
        processInput(window);
        glfwSetScrollCallback(window, scroll_callback);  

        glm::mat4 lookAt(1.0f);
        unsigned int lookAtLoc = glGetUniformLocation(shaderProgram, "lookAt");
        lookAt = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
        glUniformMatrix4fv(lookAtLoc, 1, GL_FALSE, glm::value_ptr(lookAt));
        glm::mat4 projection(1.0f);
        projection = glm::perspective(glm::radians(fov), (float)1200 / (float)720, 0.1f, 100.0f);
        unsigned int projectionLoc = glGetUniformLocation(shaderProgram, "projection");
        glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection));
        glm::mat4 hudScaleMatrix(1.0f);
        unsigned int hudScaleLoc = glGetUniformLocation(hudShader, "hudScale");
        hudScaleMatrix = glm::scale(hudScaleMatrix, glm::vec3(0.3f, 0.3f, 0.0f));
        glUniformMatrix4fv(hudScaleLoc, 1, GL_FALSE, glm::value_ptr(hudScaleMatrix));
        glm::mat4 hudTrans(1.0f);
        unsigned int hudTransLoc = glGetUniformLocation(hudShader, "hudTrans");
        hudTrans = glm::translate(hudTrans, glm::vec3(3.0f, 3.0f, 0.0f));
        glUniformMatrix4fv(hudTransLoc, 1, GL_FALSE, glm::value_ptr(hudTrans));

        glDrawArrays(GL_TRIANGLES, 0, 42);
    
        /* Swap front and back buffers */
        glfwSwapBuffers(window);

        /* Poll for and process events */
        glfwPollEvents();
    }

    glfwTerminate();
    return 0;
}

vertex shader

#version 330 core
layout(location = 0) in vec3 PlayerPositions;
layout(location = 1) in vec2 aCords;
out vec2 texCords;

uniform mat4 projection;
uniform mat4 lookAt;

void main()
{
    gl_Position = projection * lookAt * vec4(PlayerPositions, 1.0); 
    texCords = aCords;
}

fragment shader

#version 330 core

out vec4 Color;
in vec2 texCords;

uniform sampler2D playerBody;
uniform sampler2D playerFace;

void main()
{
    Color = mix(texture(playerBody, texCords), texture(playerFace, texCords), 0.2);
}

hudVertex shader

#version 330 core
layout(location = 2) in vec3 aLines;

uniform mat4 hudScale;
uniform mat4 hudTrans;

void main()
{
    gl_Position =  hudScale * hudTrans *  vec4(aLines, 1.0);
}

hudFragment shader

#version 330 core

out vec4 ColorHud;

void main()
{
    ColorHud = vec4(1.0 , 0.0, 0.5, 0.0);
}

One guy told me that this code won't work

glUseProgram(shaderProgram);
glUseProgram(hudShader);

but I don't know why. I also tried to reload the shader program by making something like this

shaderProgram = CreateShader(hudVertex, hudFragment);
glUseProgram(shaderProgram);

Why doesn't it work? How can I draw my hud (and in a future more 3D objects like enemies) with a movable camera and see all objects that I want?

\$\endgroup\$
0
\$\begingroup\$

I am a bit rusty regarding low-level OpenGL API calls but I think the problem here is your understanding of what glUseProgram actually does.

OpenGL is a state machine. It's like a rail system where the global state is determined by the current state of all railway switches. In this example, issuing a draw call is like sending a train through your railway system. Where it ends up, depends on the global state.

Now one of the relevant states in OpenGL is the currently used shader program. You set it with ´glUseProgram´. (Also see this link)

One guy told me that this code won't work

glUseProgram(shaderProgram);
glUseProgram(hudShader);

All this does is changing the state twice, where the first call is totally pointless since the resulting state is never used. So what you need to do instead is calling glUseProgram(shaderProgram); and draw your whole game scene. When you have done this, call glUseProgram(hudShader); and draw your HUD.

Also be careful in this section here:

glm::mat4 lookAt(1.0f);
unsigned int lookAtLoc = glGetUniformLocation(shaderProgram, "lookAt");
lookAt = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
glUniformMatrix4fv(lookAtLoc, 1, GL_FALSE, glm::value_ptr(lookAt));
glm::mat4 projection(1.0f);
projection = glm::perspective(glm::radians(fov), (float)1200 / (float)720, 0.1f, 100.0f);
unsigned int projectionLoc = glGetUniformLocation(shaderProgram, "projection");
glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection));
glm::mat4 hudScaleMatrix(1.0f);
unsigned int hudScaleLoc = glGetUniformLocation(hudShader, "hudScale");
hudScaleMatrix = glm::scale(hudScaleMatrix, glm::vec3(0.3f, 0.3f, 0.0f));
glUniformMatrix4fv(hudScaleLoc, 1, GL_FALSE, glm::value_ptr(hudScaleMatrix));
glm::mat4 hudTrans(1.0f);
unsigned int hudTransLoc = glGetUniformLocation(hudShader, "hudTrans");
hudTrans = glm::translate(hudTrans, glm::vec3(3.0f, 3.0f, 0.0f));
glUniformMatrix4fv(hudTransLoc, 1, GL_FALSE, glm::value_ptr(hudTrans));

As specified in the official Khronos documentation glUniformMatrix4fv works on the current program object. So this line for example:

glUniformMatrix4fv(lookAtLoc, 1, GL_FALSE, glm::value_ptr(lookAt));

most likely doesn't do what you think it does. Instead of setting uniform mat4 lookAt; in your scene vertex shader, you either set uniform mat4 hudScale; oruniform mat4 hudTrans; in your HUD shader (Depends on how the GLSL compiler enumerated them). This is because the last program you activated was your hudShader. The location you get when calling glGetUniformLocation is just an integer number that serves as an index. Usually, such mistakes cause the program to crash or to render nothing because of type or index mismatches. However, all your uniforms have the same type and you use the same number of uniforms in both shaders. You got "lucky" here. So before you set your uniforms, you have to activate the correct program first. Alternatively, in later OpenGL versions (4.?), direct state access (DSA) was added. This gives you a new set of functions to update uniforms in programs without the need to activate the corresponding program first. Here is a link to the DSA function documentation to set uniforms: click me

I don't know if there are any other issues in your program I didn't catch since I only had a closer look into the parts that are relevant to your question. So if it still not working as expected after you corrected the mentioned mistakes, feel free to ask again. However, I would highly recommend that you do some basic tutorials like these ones before you continue making a game. It is really essential to have a good understanding of how OpenGL works before you try to make a game with it. Otherwise, you will get stuck over and over again or rewrite your code thousand times just because you realize how wrong your previous approach was... Believe me, I know it since I have experience with that ;)

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Hello man, thanks for your answer. I tried to do something like this: \$\endgroup\$ – antos Aug 21 at 9:18
  • \$\begingroup\$ glUseProgram(shaderProgram) then get into while loop set all uniforms for shaderProgram. glDrawArrays -> glUseProgram(hudShader) and set all uniforms for hud Shader. In these uniforms I also add lookAt and projection. Right now I have movable camera and pink square but it isn't stuck to the screen? Any ideas? \$\endgroup\$ – antos Aug 21 at 10:03
  • \$\begingroup\$ why is your glUseProgram(shaderProgram) outside the while loop? Usually the while loop should do the following things: glUseProgram(shaderProgram) -> set all 3d scene uniforms and draw scene -> glUseProgram(shaderProgram) -> set all HUD uniforms and draw HUD -> continue with next loop iteration \$\endgroup\$ – wychmaster Aug 21 at 10:57
  • \$\begingroup\$ When I set glUseProgram(shaderProgram) what I get is Only my 3D cube. I put UseProgram(shaderProgram) in loop. I should I set 3D uniforms for hud Vertex Shader? \$\endgroup\$ – antos Aug 21 at 23:10
  • \$\begingroup\$ Ok sorry, my mistake. After set all unifrom for hudShader I didn't call glDrawArrays and that's why I didn't get any results. By the way what if I want to do many same objects? Should I use a for loop? \$\endgroup\$ – antos Aug 22 at 13:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.