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
  • 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

❌
❌