Bläddra i källkod

LibGL+LibSoftGPU: Implement `glColorMaterial` and `GL_COLOR_MATERIAL`

When `GL_COLOR_MATERIAL` is enabled, specific material parameters can
be overwritten by the current color per-vertex during the lighting
calculations. Which parameter is controlled by `glColorMaterial`.

Also move the lighting calculations _before_ clipping, because the spec
says so. As a result, we interpolate the resulting vertex color instead
of the input color.
Jelle Raaijmakers 3 år sedan
förälder
incheckning
8e935ad3b1

+ 2 - 0
Userland/Libraries/LibGL/GL/gl.h

@@ -93,6 +93,8 @@ extern "C" {
 #define GL_SHADING_LANGUAGE_VERSION 0x8B8C
 
 // Get parameters
+#define GL_COLOR_MATERIAL_FACE 0x0B55
+#define GL_COLOR_MATERIAL_MODE 0x0B56
 #define GL_COLOR_MATERIAL 0x0B57
 #define GL_FOG_START 0x0B63
 #define GL_FOG_END 0x0B64

+ 1 - 0
Userland/Libraries/LibGL/GLContext.h

@@ -115,6 +115,7 @@ public:
     virtual void gl_lightfv(GLenum light, GLenum pname, GLfloat const* params) = 0;
     virtual void gl_materialf(GLenum face, GLenum pname, GLfloat param) = 0;
     virtual void gl_materialfv(GLenum face, GLenum pname, GLfloat const* params) = 0;
+    virtual void gl_color_material(GLenum face, GLenum mode) = 0;
 
     virtual void present() = 0;
 };

+ 1 - 2
Userland/Libraries/LibGL/GLLights.cpp

@@ -14,8 +14,7 @@ extern GL::GLContext* g_gl_context;
 
 void glColorMaterial(GLenum face, GLenum mode)
 {
-    // FIXME: implement
-    dbgln_if(GL_DEBUG, "glColorMaterial({:#x}, {:#x}): unimplemented", face, mode);
+    g_gl_context->gl_color_material(face, mode);
 }
 
 void glLightf(GLenum light, GLenum pname, GLfloat param)

+ 71 - 0
Userland/Libraries/LibGL/SoftwareGLContext.cpp

@@ -91,6 +91,12 @@ Optional<ContextParameter> SoftwareGLContext::get_context_parameter(GLenum name)
         return ContextParameter { .type = GL_INT, .value = { .integer_value = static_cast<GLint>(m_blend_source_factor) } };
     case GL_BLUE_BITS:
         return ContextParameter { .type = GL_INT, .value = { .integer_value = sizeof(float) * 8 } };
+    case GL_COLOR_MATERIAL:
+        return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_color_material_enabled } };
+    case GL_COLOR_MATERIAL_FACE:
+        return ContextParameter { .type = GL_INT, .value = { .integer_value = static_cast<GLint>(m_color_material_face) } };
+    case GL_COLOR_MATERIAL_MODE:
+        return ContextParameter { .type = GL_INT, .value = { .integer_value = static_cast<GLint>(m_color_material_mode) } };
     case GL_CULL_FACE:
         return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_cull_faces } };
     case GL_DEPTH_BITS:
@@ -644,6 +650,9 @@ void SoftwareGLContext::gl_enable(GLenum capability)
     bool update_rasterizer_options = false;
 
     switch (capability) {
+    case GL_COLOR_MATERIAL:
+        m_color_material_enabled = true;
+        break;
     case GL_CULL_FACE:
         m_cull_faces = true;
         rasterizer_options.enable_culling = true;
@@ -741,6 +750,9 @@ void SoftwareGLContext::gl_disable(GLenum capability)
     bool update_rasterizer_options = false;
 
     switch (capability) {
+    case GL_COLOR_MATERIAL:
+        m_color_material_enabled = false;
+        break;
     case GL_CULL_FACE:
         m_cull_faces = false;
         rasterizer_options.enable_culling = false;
@@ -2995,6 +3007,43 @@ void SoftwareGLContext::sync_light_state()
 
     m_light_state_is_dirty = false;
 
+    auto options = m_rasterizer.options();
+    options.color_material_enabled = m_color_material_enabled;
+    switch (m_color_material_face) {
+    case GL_BACK:
+        options.color_material_face = SoftGPU::ColorMaterialFace::Back;
+        break;
+    case GL_FRONT:
+        options.color_material_face = SoftGPU::ColorMaterialFace::Front;
+        break;
+    case GL_FRONT_AND_BACK:
+        options.color_material_face = SoftGPU::ColorMaterialFace::FrontAndBack;
+        break;
+    default:
+        VERIFY_NOT_REACHED();
+    }
+    switch (m_color_material_mode) {
+    case GL_AMBIENT:
+        options.color_material_mode = SoftGPU::ColorMaterialMode::Ambient;
+        break;
+    case GL_AMBIENT_AND_DIFFUSE:
+        options.color_material_mode = SoftGPU::ColorMaterialMode::Ambient;
+        options.color_material_mode = SoftGPU::ColorMaterialMode::Diffuse;
+        break;
+    case GL_DIFFUSE:
+        options.color_material_mode = SoftGPU::ColorMaterialMode::Diffuse;
+        break;
+    case GL_EMISSION:
+        options.color_material_mode = SoftGPU::ColorMaterialMode::Emissive;
+        break;
+    case GL_SPECULAR:
+        options.color_material_mode = SoftGPU::ColorMaterialMode::Specular;
+        break;
+    default:
+        VERIFY_NOT_REACHED();
+    }
+    m_rasterizer.set_options(options);
+
     for (auto light_id = 0u; light_id < SoftGPU::NUM_LIGHTS; light_id++) {
         SoftGPU::Light light;
         auto const& current_light_state = m_light_states.at(light_id);
@@ -3249,4 +3298,26 @@ void SoftwareGLContext::gl_materialfv(GLenum face, GLenum pname, GLfloat const*
     m_light_state_is_dirty = true;
 }
 
+void SoftwareGLContext::gl_color_material(GLenum face, GLenum mode)
+{
+    APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_color_material, face, mode);
+    RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
+
+    RETURN_WITH_ERROR_IF(face != GL_FRONT
+            && face != GL_BACK
+            && face != GL_FRONT_AND_BACK,
+        GL_INVALID_ENUM);
+    RETURN_WITH_ERROR_IF(mode != GL_EMISSION
+            && mode != GL_AMBIENT
+            && mode != GL_DIFFUSE
+            && mode != GL_SPECULAR
+            && mode != GL_AMBIENT_AND_DIFFUSE,
+        GL_INVALID_ENUM);
+
+    m_color_material_face = face;
+    m_color_material_mode = mode;
+
+    m_light_state_is_dirty = true;
+}
+
 }

+ 8 - 2
Userland/Libraries/LibGL/SoftwareGLContext.h

@@ -146,6 +146,7 @@ public:
     virtual void gl_lightfv(GLenum light, GLenum pname, GLfloat const* params) override;
     virtual void gl_materialf(GLenum face, GLenum pname, GLfloat param) override;
     virtual void gl_materialfv(GLenum face, GLenum pname, GLfloat const* params) override;
+    virtual void gl_color_material(GLenum face, GLenum mode) override;
     virtual void present() override;
 
 private:
@@ -379,7 +380,8 @@ private:
             decltype(&SoftwareGLContext::gl_lightf),
             decltype(&SoftwareGLContext::gl_lightfv),
             decltype(&SoftwareGLContext::gl_materialf),
-            decltype(&SoftwareGLContext::gl_materialfv)>;
+            decltype(&SoftwareGLContext::gl_materialfv),
+            decltype(&SoftwareGLContext::gl_color_material)>;
 
         using ExtraSavedArguments = Variant<
             FloatMatrix4x4>;
@@ -431,9 +433,13 @@ private:
 
     // Lighting configuration
     bool m_lighting_enabled { false };
-
     Vector<SoftGPU::Light> m_light_states;
     Array<SoftGPU::Material, 2u> m_material_states;
+
+    // Color material
+    bool m_color_material_enabled { false };
+    GLenum m_color_material_face { GL_FRONT_AND_BACK };
+    GLenum m_color_material_mode { GL_AMBIENT_AND_DIFFUSE };
 };
 
 }

+ 58 - 31
Userland/Libraries/LibSoftGPU/Device.cpp

@@ -644,35 +644,38 @@ void Device::draw_primitives(PrimitiveType primitive_type, FloatMatrix4x4 const&
         triangle.vertices[1].normal = transform_direction(model_view_transform, triangle.vertices[1].normal);
         triangle.vertices[2].normal = transform_direction(model_view_transform, triangle.vertices[2].normal);
 
-        // Transform eye coordinates into clip coordinates using the projection transform
-        triangle.vertices[0].clip_coordinates = projection_transform * triangle.vertices[0].eye_coordinates;
-        triangle.vertices[1].clip_coordinates = projection_transform * triangle.vertices[1].eye_coordinates;
-        triangle.vertices[2].clip_coordinates = projection_transform * triangle.vertices[2].eye_coordinates;
-
-        // At this point, we're in clip space
-        // Here's where we do the clipping. This is a really crude implementation of the
-        // https://learnopengl.com/Getting-started/Coordinate-Systems
-        // "Note that if only a part of a primitive e.g. a triangle is outside the clipping volume OpenGL
-        // will reconstruct the triangle as one or more triangles to fit inside the clipping range. "
-        //
-        // ALL VERTICES ARE DEFINED IN A CLOCKWISE ORDER
-
-        // Okay, let's do some face culling first
-
-        m_clipped_vertices.clear_with_capacity();
-        m_clipped_vertices.append(triangle.vertices[0]);
-        m_clipped_vertices.append(triangle.vertices[1]);
-        m_clipped_vertices.append(triangle.vertices[2]);
-        m_clipper.clip_triangle_against_frustum(m_clipped_vertices);
-
-        if (m_clipped_vertices.size() < 3)
-            continue;
-
+        // Calculate per-vertex lighting
         if (m_options.lighting_enabled) {
-            auto const& front_material = m_materials.at(0);
-            // Walk through each vertex
-            for (auto& vertex : m_clipped_vertices) {
-                FloatVector4 result_color = front_material.emissive + (front_material.ambient * m_lighting_model.scene_ambient_color);
+            auto const& material = m_materials.at(0);
+            for (auto& vertex : triangle.vertices) {
+                auto ambient = material.ambient;
+                auto diffuse = material.diffuse;
+                auto emissive = material.emissive;
+                auto specular = material.specular;
+
+                if (m_options.color_material_enabled
+                    && (m_options.color_material_face == ColorMaterialFace::Front || m_options.color_material_face == ColorMaterialFace::FrontAndBack)) {
+                    switch (m_options.color_material_mode) {
+                    case ColorMaterialMode::Ambient:
+                        ambient = vertex.color;
+                        break;
+                    case ColorMaterialMode::AmbientAndDiffuse:
+                        ambient = vertex.color;
+                        diffuse = vertex.color;
+                        break;
+                    case ColorMaterialMode::Diffuse:
+                        diffuse = vertex.color;
+                        break;
+                    case ColorMaterialMode::Emissive:
+                        emissive = vertex.color;
+                        break;
+                    case ColorMaterialMode::Specular:
+                        specular = vertex.color;
+                        break;
+                    }
+                }
+
+                FloatVector4 result_color = emissive + (ambient * m_lighting_model.scene_ambient_color);
 
                 for (auto const& light : m_lights) {
                     if (!light.is_enabled)
@@ -716,11 +719,11 @@ void Device::draw_primitives(PrimitiveType primitive_type, FloatMatrix4x4 const&
                     (void)m_lighting_model.two_sided_lighting;
 
                     // Ambient
-                    auto const ambient_component = front_material.ambient * light.ambient_intensity;
+                    auto const ambient_component = ambient * light.ambient_intensity;
 
                     // Diffuse
                     auto const normal_dot_vertex_to_light = vertex.normal.dot(FloatVector3(vertex_to_light.x(), vertex_to_light.y(), vertex_to_light.z()));
-                    auto const diffuse_component = ((front_material.diffuse * light.diffuse_intensity) * normal_dot_vertex_to_light).clamped(0.0f, 1.0f);
+                    auto const diffuse_component = ((diffuse * light.diffuse_intensity) * normal_dot_vertex_to_light).clamped(0.0f, 1.0f);
 
                     FloatVector4 color = ambient_component;
                     color += diffuse_component;
@@ -729,11 +732,35 @@ void Device::draw_primitives(PrimitiveType primitive_type, FloatMatrix4x4 const&
                 }
 
                 vertex.color = result_color;
-                vertex.color.set_w(front_material.diffuse.w()); // OpenGL 1.5 spec, page 59: "The A produced by lighting is the alpha value associated with diffuse color material"
+                vertex.color.set_w(diffuse.w()); // OpenGL 1.5 spec, page 59: "The A produced by lighting is the alpha value associated with diffuse color material"
                 vertex.color.clamp(0.0f, 1.0f);
             }
         }
 
+        // Transform eye coordinates into clip coordinates using the projection transform
+        triangle.vertices[0].clip_coordinates = projection_transform * triangle.vertices[0].eye_coordinates;
+        triangle.vertices[1].clip_coordinates = projection_transform * triangle.vertices[1].eye_coordinates;
+        triangle.vertices[2].clip_coordinates = projection_transform * triangle.vertices[2].eye_coordinates;
+
+        // At this point, we're in clip space
+        // Here's where we do the clipping. This is a really crude implementation of the
+        // https://learnopengl.com/Getting-started/Coordinate-Systems
+        // "Note that if only a part of a primitive e.g. a triangle is outside the clipping volume OpenGL
+        // will reconstruct the triangle as one or more triangles to fit inside the clipping range. "
+        //
+        // ALL VERTICES ARE DEFINED IN A CLOCKWISE ORDER
+
+        // Okay, let's do some face culling first
+
+        m_clipped_vertices.clear_with_capacity();
+        m_clipped_vertices.append(triangle.vertices[0]);
+        m_clipped_vertices.append(triangle.vertices[1]);
+        m_clipped_vertices.append(triangle.vertices[2]);
+        m_clipper.clip_triangle_against_frustum(m_clipped_vertices);
+
+        if (m_clipped_vertices.size() < 3)
+            continue;
+
         for (auto& vec : m_clipped_vertices) {
             // To normalized device coordinates (NDC)
             auto const one_over_w = 1 / vec.clip_coordinates.w();

+ 3 - 0
Userland/Libraries/LibSoftGPU/Device.h

@@ -70,6 +70,9 @@ struct RasterizerOptions {
     Array<TexCoordGenerationConfig, 4> texcoord_generation_config {};
     Gfx::IntRect viewport;
     bool lighting_enabled { false };
+    bool color_material_enabled { false };
+    ColorMaterialFace color_material_face { ColorMaterialFace::FrontAndBack };
+    ColorMaterialMode color_material_mode { ColorMaterialMode::AmbientAndDiffuse };
 };
 
 struct LightModelParameters {

+ 14 - 0
Userland/Libraries/LibSoftGPU/Enums.h

@@ -33,6 +33,20 @@ enum class BlendFactor {
     SrcAlphaSaturate,
 };
 
+enum class ColorMaterialFace {
+    Front,
+    Back,
+    FrontAndBack,
+};
+
+enum class ColorMaterialMode {
+    Ambient,
+    AmbientAndDiffuse,
+    Diffuse,
+    Emissive,
+    Specular,
+};
+
 enum class DepthTestFunction {
     Never,
     Always,