FreshRSS

Normální zobrazení

Jsou dostupné nové články, klikněte pro obnovení stránky.
PředevčíremHlavní kanál
  • ✇Recent Questions - Game Development Stack Exchange
  • How to create a SIMPLE Skybox using OpenGL and SDLガブリエル Gabriel
    As the title is saying I'm trying to make a simple skybox to learn how it works. Using, of course OpenGL and SDL. I have tried read some sites, here are them: link 1 link 2 link 3. None of them were of good use, since they are poorly written or simply too much complex. For example one of them use glfw instead of SDL, while another use another way that I didn't understand and wouldn't use anyways, because I don't want to rewrite all my code JUST so that I can try it. Or they also use a camera imp
     

How to create a SIMPLE Skybox using OpenGL and SDL

As the title is saying I'm trying to make a simple skybox to learn how it works. Using, of course OpenGL and SDL.

I have tried read some sites, here are them: link 1 link 2 link 3.

None of them were of good use, since they are poorly written or simply too much complex. For example one of them use glfw instead of SDL, while another use another way that I didn't understand and wouldn't use anyways, because I don't want to rewrite all my code JUST so that I can try it.

Or they also use a camera implementation (all of the 3 of this links, If I'm not mistaken), but I don't know if I necessarily need a camera, since my objective isn't moving or rotating around, it's just to draw the skybox.

Also they use shaders. Which I also don't know if is necessary, but I tried to do implement them anyway. All of my attempts did compile and didn't show any errors but it also didn't draw anything, just a grey screen. Probably because of "glClearColor(0.1f, 0.1f, 0.1f, 1.0f)" as some people said (I searched and read about similar problems), they also said that maybe it was a geometry problem, I don't know if it's and how to fix it, since no error is shown.

Also, don't ask me the code of each attempt because I haven't, they are all around, some commented, some I deleted and don't remember anymore, I simply can't put it all together anymore. But from reading the links and "to prove" (I guess) I got some understanding of it, so instead I will share my understanding.

To draw something or in this case the skybox I would need:
1° Thing, create the VAO and VBO:

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

2° Thing, is generating a texture ID and binding a texture to it and some parameters:

unsigned int ID;
unsigned char *data = //load image here
int width, height;
glGenTextures(1, &ID);
glBindTexture(GL_TEXTURE_CUBE_MAP, ID);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

*image here is refering to the loaded image, one of the links use stb_image and other SDL_LoadBMP for example. I tried all of them, I don't know if it's relevant to use the one that the tutorial is using, while I tried them, I also put in error checks that the tutorials gave and no error was give whatsoever. The book that I'm reading uses SOIL_load_image though.

SOIL_load_image("path", &width, &height, 0, SOIL_LOAD_AUTO);
//So:
data = SOIL_load_image("path", &width, &height, 0, SOIL_LOAD_AUTO);

*Also, a couple of observations:
In the case of link1, they use gluBuild2DMipmaps, with GL_TEXTURE_2D.
In the case of link2, they use the above glTexImage2D, with GL_TEXTURE_CUBE_MAP_POSITIVE_X + i. i goes from 0 to 5, each face of a cube.
In the case of link3, they use glTexImage2D, with GL_TEXTURE_2D, but individually for each face.

3° Thing, is then drawing in the main loop:

//glEnable(GL_DEPTH_TEST); One of the tutorials says that I should use this before drawing.

//Loop
    glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

    glBindVertexArray(VAO);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_CUBE_MAP, texture);
    glDrawArrays(GL_TRIANGLES, 0, 36);
    glBindVertexArray(0);

    SDL_GL_SwapWindow(mWindow);
//

*One of the tutorials just draw a cube right away, I also tried this, no success:

//repeate for each face, here the tutorial obviously generates each face texture ID individually. 
   glBindTexture(GL_TEXTURE_2D, face_textureID);
       glBegin(GL_QUADS);
       glTexCoord2f(0,0);
       glVertex3f(size/2,size/2,size/2);
       glTexCoord2f(1,0);  
       glVertex3f(-size/2,size/2,size/2);
       glTexCoord2f(1,1);
       glVertex3f(-size/2,-size/2,size/2);
       glTexCoord2f(0,1);
       glVertex3f(size/2,-size/2,size/2);
    glEnd();
//

*Here a lot happens on the tutorials I read that I don't understand or don't know the purpose of.

//Some tutorials use those functions
glDepthFunc(GL_LESS);
glDepthFunc(GL_LEQUAL);

glLoadIdentity();

glLightfv(GL_LIGHT0,GL_POSITION,pos); //float pos[]={-1.0,1.0,-2.0,1.0};

glEnable(GL_LIGHTING);
//and
glDisable(GL_LIGHTING);

glEnable(GL_DEPTH_TEST);
//and
glDisable(GL_DEPTH_TEST);
   
glEnable(GL_TEXTURE_2D);
 

*In addition. Shaders.

unsigned int vertex, fragment;
unsigned int shaderID;

//Example of a shader of a tutorial
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"out vec4 vertexColor;\n"
"void main()\n"
"{\n"
"    gl_Position = vec4(aPos, 1.0);\n"
"    vertexColor = vec4(0.9, 0.5, 0.0, 1.0);\n"
"}\0";  

vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vertexShaderSource, NULL);
glCompileShader(vertex);

fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fragmentShaderSource, NULL);
glCompileShader(fragment);

shaderID = glCreateProgram();
glAttachShader(Cubemap_Shader_ID, vertex);
glAttachShader(Cubemap_Shader_ID, fragment);
glLinkProgram(Cubemap_Shader_ID);

glDeleteShader(vertex);
glDeleteShader(fragment);  

glUseProgram(shaderID);

//Draw. Bind VAO, activate texture, bind texture, draw arrays and bind arrays

SDL_GL_SwapWindow(mWindow);

I will even give the entire code I used, to those out there that cry about a "minimal reproducible code". The code used here is from "Game Programming in C++: Creating 3D Games (Game Design) 1st Edition by Sanjay Madhav" if you're wondering or if you want to see more of it.

//game.h
#pragma once

#include "../Header Files/SDL/SDL_types.h"

#include <unordered_map>
#include <string>
#include <vector>

class Game
{
public:
    Game();
    bool Initialize();
    void RunLoop();
    void Shutdown();

    void ProcessInput();

private:
    bool mIsRunning;

    class Renderer* mRenderer;
};

//renderer.h
#pragma once

#include "../Header Files/SDL/SDL.h"

#include <string>
#include <vector>
#include <unordered_map>

class Renderer
{
public:
    Renderer(class Game* game);
    ~Renderer();

    bool Initialize(float screenW, float screenH);
    void Shutdown();

    void Init_Things();
    void Draw();

private:
    class Game* mGame;

    SDL_Window* mWindow;

    SDL_GLContext mContext;

    float mScreenW;
    float mScreenH;

    //Here you should create VAO, VBO, shader and the textureID so that you can use initialize them and use in the drawing function. For example:    
    //unsigned int VAO;
    //unsigned int VBO;
    //unsigned int shader;
    //unsigned int textureID
}
//main.cpp
#include "../Header Files/Game.h"

int main(int argc, char *argv[])
{
    Game game;

    bool success = game.Initialize();

    if (success)
    {
        game.RunLoop();
    }

    game.Shutdown();
    return 0;
};

//game.cpp
#include "../Header Files/Game.h"
#include "../Header Files/Renderer.h"

#include "../Header Files/SDL/SDL.h"

#include <algorithm>

Game::Game() :mRenderer(nullptr), mIsRunning(true) {}

bool Game::Initialize()
{
    if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) != 0)
    {
        SDL_Log("Unable to initialize SDL: %s", SDL_GetError());
        return false;
    }

    mRenderer = new Renderer(this);
    if (!mRenderer->Initialize(1024.0f, 768.0f))
    {
        SDL_Log("Failed to initialize renderer");
        delete mRenderer;
        mRenderer = nullptr;
        return false;
    }

    mRenderer->Init_Things();
    //I put steps 1° and 2° here.

    
    return true;
}

void Game::RunLoop() 
{
    while (mIsRunning)
    {
        ProcessInput();
        mRenderer->Draw();
        //Here things are drawn.
    }
}

void Game::Shutdown() 
{
    if (mRenderer)
    {
        mRenderer->Shutdown();
    }

    SDL_Quit();
}

void Game::ProcessInput()
{
    SDL_Event event;
    
    while (SDL_PollEvent(&event))
    {
        switch (event.type)
        {
            case SDL_QUIT:
                mIsRunning = false;
                break;
        }
    }
    
    const Uint8* state = SDL_GetKeyboardState(NULL);
    if (state[SDL_SCANCODE_ESCAPE])
    {
        mIsRunning = false;
    }
}

#include "../Header Files/Game.h"
#include "../Header Files/Renderer.h"

#include <algorithm>

#include "../Header Files/GL/glew.h"
#include "../Header Files/SOIL/SOIL.h"

Renderer::Renderer(Game* game) :mGame(game) {}

Renderer::~Renderer() {}

bool Renderer::Initialize(float screenW, float screenH)
{
    mScreenW = screenW;
    mScreenH = screenH;

    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);

    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);

    SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);

    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

    SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);

    mWindow = SDL_CreateWindow("Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, static_cast<int>(mScreenW), static_cast<int>(mScreenH), SDL_WINDOW_OPENGL);
    if (!mWindow)
    {
        SDL_Log("Failed to create window: %s", SDL_GetError());
        return false;
    }

    mContext = SDL_GL_CreateContext(mWindow);

    glewExperimental = GL_TRUE;
    if (glewInit() != GLEW_OK)
    {
        SDL_Log("Failed to initialize GLEW.");
        return false;
    }

    glGetError();

    return true;
}

void Renderer::Shutdown()
{
    //Here you can delete the VAO, VBO and shaders. Using:
    //glDeleteVertexArrays(1, &VAO);
    //glDeleteBuffers(1, &VBO);
    //glDeleteProgram(shader);

    SDL_GL_DeleteContext(mContext);
    SDL_DestroyWindow(mWindow);
}

void Renderer::Init_Things()
{
    //Here I would put steps 1° and 2°.

    //One of the tutorials say that I should activate the shader(s) in the initialization (before drawing), like for example:
    //glUseProgram(CubemapShaderID); 

    //glUseProgram(SkyboxShaderID);    
}

void Renderer::Draw()
{
    //glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
    //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    //Here I would put step 3° (Drawing).

    //SDL_GL_SwapWindow(mWindow);
}

TL;DR and as clear as possible. How to make a SIMPLE Skybox. Without a camera and shader(s), only if necessary.

And if necessary, please 😭 just a simple shader (without Reflection, Refraction, etc.), and a simple camera implementation (without translation, rotation, etc.)...

  • ✇Recent Questions - Game Development Stack Exchange
  • Why is my Gouraud Shading not working correctly?Finn
    I am trying to write a 3d Renderer in C using SDL and cgml but my shading seems to be not working correctly. When rendering the teapot I get seams and when I want to render a cube I get a bright white line between my triangles. I am pretty sure that I calculate my normals correctly but I can show that code too if necessary. All my math functions like v3_normalise / cam_perspective should be correct too because they are just aliases for cgml functions. static void _draw_triangle(SDL_Renderer* re
     

Why is my Gouraud Shading not working correctly?

I am trying to write a 3d Renderer in C using SDL and cgml but my shading seems to be not working correctly. When rendering the teapot I get seams and when I want to render a cube I get a bright white line between my triangles. I am pretty sure that I calculate my normals correctly but I can show that code too if necessary. All my math functions like v3_normalise / cam_perspective should be correct too because they are just aliases for cgml functions.enter image description here

enter image description here

static void _draw_triangle(SDL_Renderer* renderer, int x1, int y1, int x2, int y2, int x3, int y3) {
    SDL_RenderLine(renderer, x1, y1, x2, y2);
    SDL_RenderLine(renderer, x2, y2, x3, y3);
    SDL_RenderLine(renderer, x3, y3, x1, y1);
}

static int32_t _compare_triangles(const void* a, const void* b) {
    triangle_t* triangle_a = (triangle_t*)a;
    triangle_t* triangle_b = (triangle_t*)b;

    float z1 = (triangle_a->vertices[0].z + triangle_a->vertices[1].z + triangle_a->vertices[2].z) / 3.0f;
    float z2 = (triangle_b->vertices[0].z + triangle_b->vertices[1].z + triangle_b->vertices[2].z) / 3.0f;

    float comparison = z2 - z1;
    if (comparison < 0.0f) return 1;
    if (comparison > 0.0f) return -1;
    return 0;
}

static void _render_triangles_mesh(SDL_Renderer* renderer, triangle_t* triangles_to_render, size_t num_triangles_to_render) {
    SDL_SetRenderDrawColor(renderer, 0xFF, 0, 0xFF, 0xFF);  // set color to pink

    for (size_t j = 0; j < num_triangles_to_render; j++) {
        triangle_t triangle = dynamic_array_at(triangles_to_render, j);

        _draw_triangle(
            renderer,
            triangle.vertices[0].x, triangle.vertices[0].y, 
            triangle.vertices[1].x, triangle.vertices[1].y,
            triangle.vertices[2].x, triangle.vertices[2].y
        );
    }
}

static v3i _calc_vertex_color(v3 normal, v3 light_direction) {
    float intensity = glm_max(0.1f, v3_dot(normal, light_direction)) * 1.5f;
    v3i color = v3i_of((int32_t)glm_clamp(255.0f * intensity, 0.0f, 255.0f));

    return color;
}

static void _render_triangles_filled(SDL_Renderer* renderer, triangle_t* triangles_to_render, size_t num_triangles_to_render, v3* vertex_normals, v3 light_direction) {
    /* convert to SDL_Vertex triangles and add to vertices to render */
    SDL_Vertex vertices[num_triangles_to_render * 3];

    for (size_t i = 0; i < num_triangles_to_render; i++) {
        triangle_t triangle = dynamic_array_at(triangles_to_render, i);

        for (size_t j = 0; j < 3; j++) {
            v3i color = _calc_vertex_color(vertex_normals[triangle.indices[j]], light_direction);

            /* add vertex to SDL vertices */
            SDL_Vertex vertex = {
                .position = {triangle.vertices[j].x, triangle.vertices[j].y}, 
                .color = {color.r, color.g, color.b, 0xFF}
            };
            vertices[i * 3 + j] = vertex;
        }
    }
        
    /* render triangles */
    SDL_RenderGeometry(renderer, NULL, vertices, num_triangles_to_render * 3, NULL, 0); 
}

int32_t render(state_t* state, mesh_t* mesh, v3 object_offset) {
    static float alpha = 0;
    alpha += 0.6f * state->time.delta_sec;

    m4 rotation_matrix = glms_euler_xyz(v3_of(0.0f, alpha, 0.0f)); // glms_euler_xyz(v3_of(alpha * 0.5f, 0.0f, alpha));
    m4 translation_matrix = glms_translate_make(object_offset);
    m4 world_matrix = m4_mul(translation_matrix, rotation_matrix);

    v3 up = v3_of(0.0f, 1.0f, 0.0f);
    v3 target = v3_add(state->engine.camera.position, state->engine.camera.direction);
    m4 camera_matrix = cam_lookat(state->engine.camera.position, target, up);
    m4 view_matrix = glms_inv_tr(camera_matrix);
    
    triangle_t* triangles_to_render = dynamic_array_create(triangle_t);

    for (size_t i = 0; i < mesh->num_triangles; i++) {
        triangle_t triangle = dynamic_array_at(mesh->triangles, i);
        triangle_t triangle_transformed, triangle_projected, triangle_viewed;

        /* rotate and translate triangle */
        triangle_transformed = triangle;
        triangle_transformed.vertices[0] = m4_mulv(world_matrix, triangle.vertices[0]);
        triangle_transformed.vertices[1] = m4_mulv(world_matrix, triangle.vertices[1]);
        triangle_transformed.vertices[2] = m4_mulv(world_matrix, triangle.vertices[2]);
    
        /* world space to camera space */
        triangle_viewed = triangle_transformed;
        triangle_viewed.vertices[0] = m4_mulv(view_matrix, triangle_transformed.vertices[0]);
        triangle_viewed.vertices[1] = m4_mulv(view_matrix, triangle_transformed.vertices[1]);
        triangle_viewed.vertices[2] = m4_mulv(view_matrix, triangle_transformed.vertices[2]);

        /* 3d to 2d */
        triangle_projected = triangle_viewed;
        triangle_projected.vertices[0] = m4_mulv(state->engine.projection_matrix, triangle_viewed.vertices[0]);
        triangle_projected.vertices[1] = m4_mulv(state->engine.projection_matrix, triangle_viewed.vertices[1]);
        triangle_projected.vertices[2] = m4_mulv(state->engine.projection_matrix, triangle_viewed.vertices[2]);
        triangle_projected.vertices[0] = v4_divs(triangle_projected.vertices[0], triangle_projected.vertices[0].w);
        triangle_projected.vertices[1] = v4_divs(triangle_projected.vertices[1], triangle_projected.vertices[1].w);
        triangle_projected.vertices[2] = v4_divs(triangle_projected.vertices[2], triangle_projected.vertices[2].w);

        /* backface culling using winding order */
        v3 line1 = v3_sub(v3_from(triangle_projected.vertices[1]), v3_from(triangle_projected.vertices[0]));
        v3 line2 = v3_sub(v3_from(triangle_projected.vertices[2]), v3_from(triangle_projected.vertices[0]));
        float sign = line1.x * line2.y - line2.x * line1.y;
        if (sign > 0.0f) continue;
        
        /* scale into view */
        triangle_projected.vertices[0].x = map(triangle_projected.vertices[0].x, -1.0f, 1.0f, 0, WIDTH);
        triangle_projected.vertices[0].y = map(triangle_projected.vertices[0].y, -1.0f, 1.0f, 0, HEIGHT);
        triangle_projected.vertices[1].x = map(triangle_projected.vertices[1].x, -1.0f, 1.0f, 0, WIDTH);
        triangle_projected.vertices[1].y = map(triangle_projected.vertices[1].y, -1.0f, 1.0f, 0, HEIGHT);
        triangle_projected.vertices[2].x = map(triangle_projected.vertices[2].x, -1.0f, 1.0f, 0, WIDTH);
        triangle_projected.vertices[2].y = map(triangle_projected.vertices[2].y, -1.0f, 1.0f, 0, HEIGHT);
        
        /* add triangle to list */
        dynamic_array_append(triangles_to_render, triangle_projected);
    }

    /* transform vertex normals */
    v3* transformed_normals = malloc(sizeof(v3) * mesh->num_vertices);
    m3 normal_matrix = m3_inv(m4_pick3t(world_matrix));
    
    for (size_t i = 0; i < mesh->num_vertices; i++) {
        v3 normal = dynamic_array_at(mesh->vertex_normals, i);
        v3 normal_transformed = v3_normalize(m3_mulv(normal_matrix, normal));
        transformed_normals[i] = normal_transformed;
    }

    /* sort triangles back to front */
    size_t num_triangles_to_render = dynamic_array_get_length(triangles_to_render);
    qsort(triangles_to_render, num_triangles_to_render, sizeof(triangle_t), _compare_triangles);

    /* draw triangles */
    #ifdef DEBUG
        _render_triangles_mesh(state->renderer, triangles_to_render, num_triangles_to_render);
    #else
        _render_triangles_filled(state->renderer, triangles_to_render, num_triangles_to_render, transformed_normals, state->engine.light.direction);
    #endif
    
    /* cleanup */
    free(transformed_normals);
    dynamic_array_destroy(triangles_to_render);

    return state->retval;
}

EDIT: Image of the normals

enter image description here

❌
❌