mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 05:37:35 +00:00
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.
This commit is contained in:
parent
9d4c2f6308
commit
8e935ad3b1
8 changed files with 158 additions and 35 deletions
|
@ -93,6 +93,8 @@ extern "C" {
|
||||||
#define GL_SHADING_LANGUAGE_VERSION 0x8B8C
|
#define GL_SHADING_LANGUAGE_VERSION 0x8B8C
|
||||||
|
|
||||||
// Get parameters
|
// Get parameters
|
||||||
|
#define GL_COLOR_MATERIAL_FACE 0x0B55
|
||||||
|
#define GL_COLOR_MATERIAL_MODE 0x0B56
|
||||||
#define GL_COLOR_MATERIAL 0x0B57
|
#define GL_COLOR_MATERIAL 0x0B57
|
||||||
#define GL_FOG_START 0x0B63
|
#define GL_FOG_START 0x0B63
|
||||||
#define GL_FOG_END 0x0B64
|
#define GL_FOG_END 0x0B64
|
||||||
|
|
|
@ -115,6 +115,7 @@ public:
|
||||||
virtual void gl_lightfv(GLenum light, GLenum pname, GLfloat const* params) = 0;
|
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_materialf(GLenum face, GLenum pname, GLfloat param) = 0;
|
||||||
virtual void gl_materialfv(GLenum face, GLenum pname, GLfloat const* params) = 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;
|
virtual void present() = 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,8 +14,7 @@ extern GL::GLContext* g_gl_context;
|
||||||
|
|
||||||
void glColorMaterial(GLenum face, GLenum mode)
|
void glColorMaterial(GLenum face, GLenum mode)
|
||||||
{
|
{
|
||||||
// FIXME: implement
|
g_gl_context->gl_color_material(face, mode);
|
||||||
dbgln_if(GL_DEBUG, "glColorMaterial({:#x}, {:#x}): unimplemented", face, mode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void glLightf(GLenum light, GLenum pname, GLfloat param)
|
void glLightf(GLenum light, GLenum pname, GLfloat param)
|
||||||
|
|
|
@ -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) } };
|
return ContextParameter { .type = GL_INT, .value = { .integer_value = static_cast<GLint>(m_blend_source_factor) } };
|
||||||
case GL_BLUE_BITS:
|
case GL_BLUE_BITS:
|
||||||
return ContextParameter { .type = GL_INT, .value = { .integer_value = sizeof(float) * 8 } };
|
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:
|
case GL_CULL_FACE:
|
||||||
return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_cull_faces } };
|
return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_cull_faces } };
|
||||||
case GL_DEPTH_BITS:
|
case GL_DEPTH_BITS:
|
||||||
|
@ -644,6 +650,9 @@ void SoftwareGLContext::gl_enable(GLenum capability)
|
||||||
bool update_rasterizer_options = false;
|
bool update_rasterizer_options = false;
|
||||||
|
|
||||||
switch (capability) {
|
switch (capability) {
|
||||||
|
case GL_COLOR_MATERIAL:
|
||||||
|
m_color_material_enabled = true;
|
||||||
|
break;
|
||||||
case GL_CULL_FACE:
|
case GL_CULL_FACE:
|
||||||
m_cull_faces = true;
|
m_cull_faces = true;
|
||||||
rasterizer_options.enable_culling = true;
|
rasterizer_options.enable_culling = true;
|
||||||
|
@ -741,6 +750,9 @@ void SoftwareGLContext::gl_disable(GLenum capability)
|
||||||
bool update_rasterizer_options = false;
|
bool update_rasterizer_options = false;
|
||||||
|
|
||||||
switch (capability) {
|
switch (capability) {
|
||||||
|
case GL_COLOR_MATERIAL:
|
||||||
|
m_color_material_enabled = false;
|
||||||
|
break;
|
||||||
case GL_CULL_FACE:
|
case GL_CULL_FACE:
|
||||||
m_cull_faces = false;
|
m_cull_faces = false;
|
||||||
rasterizer_options.enable_culling = false;
|
rasterizer_options.enable_culling = false;
|
||||||
|
@ -2995,6 +3007,43 @@ void SoftwareGLContext::sync_light_state()
|
||||||
|
|
||||||
m_light_state_is_dirty = false;
|
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++) {
|
for (auto light_id = 0u; light_id < SoftGPU::NUM_LIGHTS; light_id++) {
|
||||||
SoftGPU::Light light;
|
SoftGPU::Light light;
|
||||||
auto const& current_light_state = m_light_states.at(light_id);
|
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;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,6 +146,7 @@ public:
|
||||||
virtual void gl_lightfv(GLenum light, GLenum pname, GLfloat const* params) override;
|
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_materialf(GLenum face, GLenum pname, GLfloat param) override;
|
||||||
virtual void gl_materialfv(GLenum face, GLenum pname, GLfloat const* params) 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;
|
virtual void present() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -379,7 +380,8 @@ private:
|
||||||
decltype(&SoftwareGLContext::gl_lightf),
|
decltype(&SoftwareGLContext::gl_lightf),
|
||||||
decltype(&SoftwareGLContext::gl_lightfv),
|
decltype(&SoftwareGLContext::gl_lightfv),
|
||||||
decltype(&SoftwareGLContext::gl_materialf),
|
decltype(&SoftwareGLContext::gl_materialf),
|
||||||
decltype(&SoftwareGLContext::gl_materialfv)>;
|
decltype(&SoftwareGLContext::gl_materialfv),
|
||||||
|
decltype(&SoftwareGLContext::gl_color_material)>;
|
||||||
|
|
||||||
using ExtraSavedArguments = Variant<
|
using ExtraSavedArguments = Variant<
|
||||||
FloatMatrix4x4>;
|
FloatMatrix4x4>;
|
||||||
|
@ -431,9 +433,13 @@ private:
|
||||||
|
|
||||||
// Lighting configuration
|
// Lighting configuration
|
||||||
bool m_lighting_enabled { false };
|
bool m_lighting_enabled { false };
|
||||||
|
|
||||||
Vector<SoftGPU::Light> m_light_states;
|
Vector<SoftGPU::Light> m_light_states;
|
||||||
Array<SoftGPU::Material, 2u> m_material_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 };
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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[1].normal = transform_direction(model_view_transform, triangle.vertices[1].normal);
|
||||||
triangle.vertices[2].normal = transform_direction(model_view_transform, triangle.vertices[2].normal);
|
triangle.vertices[2].normal = transform_direction(model_view_transform, triangle.vertices[2].normal);
|
||||||
|
|
||||||
// Transform eye coordinates into clip coordinates using the projection transform
|
// Calculate per-vertex lighting
|
||||||
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;
|
|
||||||
|
|
||||||
if (m_options.lighting_enabled) {
|
if (m_options.lighting_enabled) {
|
||||||
auto const& front_material = m_materials.at(0);
|
auto const& material = m_materials.at(0);
|
||||||
// Walk through each vertex
|
for (auto& vertex : triangle.vertices) {
|
||||||
for (auto& vertex : m_clipped_vertices) {
|
auto ambient = material.ambient;
|
||||||
FloatVector4 result_color = front_material.emissive + (front_material.ambient * m_lighting_model.scene_ambient_color);
|
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) {
|
for (auto const& light : m_lights) {
|
||||||
if (!light.is_enabled)
|
if (!light.is_enabled)
|
||||||
|
@ -716,11 +719,11 @@ void Device::draw_primitives(PrimitiveType primitive_type, FloatMatrix4x4 const&
|
||||||
(void)m_lighting_model.two_sided_lighting;
|
(void)m_lighting_model.two_sided_lighting;
|
||||||
|
|
||||||
// Ambient
|
// Ambient
|
||||||
auto const ambient_component = front_material.ambient * light.ambient_intensity;
|
auto const ambient_component = ambient * light.ambient_intensity;
|
||||||
|
|
||||||
// Diffuse
|
// 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 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;
|
FloatVector4 color = ambient_component;
|
||||||
color += diffuse_component;
|
color += diffuse_component;
|
||||||
|
@ -729,11 +732,35 @@ void Device::draw_primitives(PrimitiveType primitive_type, FloatMatrix4x4 const&
|
||||||
}
|
}
|
||||||
|
|
||||||
vertex.color = result_color;
|
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);
|
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) {
|
for (auto& vec : m_clipped_vertices) {
|
||||||
// To normalized device coordinates (NDC)
|
// To normalized device coordinates (NDC)
|
||||||
auto const one_over_w = 1 / vec.clip_coordinates.w();
|
auto const one_over_w = 1 / vec.clip_coordinates.w();
|
||||||
|
|
|
@ -70,6 +70,9 @@ struct RasterizerOptions {
|
||||||
Array<TexCoordGenerationConfig, 4> texcoord_generation_config {};
|
Array<TexCoordGenerationConfig, 4> texcoord_generation_config {};
|
||||||
Gfx::IntRect viewport;
|
Gfx::IntRect viewport;
|
||||||
bool lighting_enabled { false };
|
bool lighting_enabled { false };
|
||||||
|
bool color_material_enabled { false };
|
||||||
|
ColorMaterialFace color_material_face { ColorMaterialFace::FrontAndBack };
|
||||||
|
ColorMaterialMode color_material_mode { ColorMaterialMode::AmbientAndDiffuse };
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LightModelParameters {
|
struct LightModelParameters {
|
||||||
|
|
|
@ -33,6 +33,20 @@ enum class BlendFactor {
|
||||||
SrcAlphaSaturate,
|
SrcAlphaSaturate,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class ColorMaterialFace {
|
||||||
|
Front,
|
||||||
|
Back,
|
||||||
|
FrontAndBack,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ColorMaterialMode {
|
||||||
|
Ambient,
|
||||||
|
AmbientAndDiffuse,
|
||||||
|
Diffuse,
|
||||||
|
Emissive,
|
||||||
|
Specular,
|
||||||
|
};
|
||||||
|
|
||||||
enum class DepthTestFunction {
|
enum class DepthTestFunction {
|
||||||
Never,
|
Never,
|
||||||
Always,
|
Always,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue