Archived
1
Fork 0

Remove correction_matrix, produce left-handed transforms, and fix PCSS shadowing

This commit is contained in:
redstrate 2020-08-18 00:35:49 -04:00
parent 519e4cb9b1
commit b2deff18aa
15 changed files with 96 additions and 75 deletions

View file

@ -119,6 +119,15 @@ MTLSamplerMinMagFilter toFilter(GFXFilter filter) {
}
}
MTLWinding toWinding(GFXWindingMode mode) {
switch(mode) {
case GFXWindingMode::Clockwise:
return MTLWindingClockwise;
case GFXWindingMode::CounterClockwise:
return MTLWindingCounterClockwise;
}
}
bool GFXMetal::is_supported() {
return true;
}
@ -584,10 +593,11 @@ GFXPipeline* GFXMetal::create_graphics_pipeline(const GFXGraphicsPipelineCreateI
pipeline->pushConstantIndex = binding.binding;
}
pipeline->winding_mode = info.rasterization.winding_mode;
MTLDepthStencilDescriptor* depthStencil = [MTLDepthStencilDescriptor new];
if(info.depth.depth_mode != GFXDepthMode::None) {
//depthStencil.depthCompareFunction = info.depth.depth_mode == GFXDepthMode::LessOrEqual ? MTLCompareFunctionGreaterEqual : MTLCompareFunctionGreater;
depthStencil.depthCompareFunction = info.depth.depth_mode == GFXDepthMode::LessOrEqual ? MTLCompareFunctionLessEqual : MTLCompareFunctionLess;
depthStencil.depthWriteEnabled = true;
}
@ -760,6 +770,7 @@ void GFXMetal::submit(GFXCommandBuffer* command_buffer, const int window) {
[renderEncoder setDepthStencilState:currentPipeline->depthStencil];
[renderEncoder setCullMode:((GFXMetalPipeline*)command.data.set_pipeline.pipeline)->cullMode];
[renderEncoder setFrontFacingWinding:toWinding(((GFXMetalPipeline*)command.data.set_pipeline.pipeline)->winding_mode)];
if(currentPipeline->renderWire)
[renderEncoder setTriangleFillMode:MTLTriangleFillModeLines];

View file

@ -13,7 +13,8 @@ public:
MTLPrimitiveType primitiveType;
MTLCullMode cullMode;
GFXWindingMode winding_mode;
struct VertexStride {
int location, stride;
};

View file

@ -88,6 +88,11 @@ enum class GFXCullingMode {
None
};
enum class GFXWindingMode {
Clockwise,
CounterClockwise
};
struct GFXVertexInput {
int location = 0;
int stride = 0;
@ -158,6 +163,8 @@ struct GFXGraphicsPipelineCreateInfo {
GFXPolygonType polygon_type = GFXPolygonType::Fill;
GFXCullingMode culling_mode = GFXCullingMode::None;
GFXWindingMode winding_mode = GFXWindingMode::Clockwise;
} rasterization;
struct Blending {

View file

@ -5,15 +5,11 @@
#include "quaternion.hpp"
namespace transform {
/*
Produces a right-handed perspective matrix.
*/
Matrix4x4 perspective(const float fov, const float aspectRatio, const float zNear);
Matrix4x4 perspective(const float field_of_view, const float aspect, const float z_near, const float z_far);
Matrix4x4 infinite_perspective(const float field_of_view, const float aspect, const float z_near);
Matrix4x4 orthographic(float left, float right, float bottom, float top, float zNear, float zFar);
/*
Produces right-handed outputs.
*/
Matrix4x4 look_at(const Vector3 eye, const Vector3 center, const Vector3 up);
Quaternion quat_look_at(const Vector3 eye, const Vector3 center, const Vector3 up);

View file

@ -4,19 +4,36 @@
#include "math.hpp"
Matrix4x4 transform::perspective(const float fov, const float aspect, const float zNear) {
const float range = tanf(fov / 2.0f) * zNear;
/*
These produce left-handed matrices.
Metal/DX12 are both NDC +y down.
*/
Matrix4x4 transform::perspective(const float field_of_view, const float aspect, const float z_near, const float z_far) {
const float tan_half_fov = tanf(field_of_view / 2.0f);
Matrix4x4 result(0.0f);
result[0][0] = 1.0f / (aspect * tan_half_fov);
result[1][1] = 1.0f / tan_half_fov;
result[2][2] = z_far / (z_far - z_near);
result[2][3] = 1.0f;
result[3][2] = -(z_far * z_near) / (z_far - z_near);
return result;
}
Matrix4x4 transform::infinite_perspective(const float field_of_view, const float aspect, const float z_near) {
const float range = tanf(field_of_view / 2.0f) * z_near;
const float left = -range * aspect;
const float right = range * aspect;
const float bottom = -range;
const float top = range;
Matrix4x4 result(0.0f);
result[0][0] = (2.0f * zNear) / (right - left);
result[1][1] = (2.0f * zNear) / (top - bottom);
result[2][2] = -1.0f;
result[2][3] = -1.0f;
result[3][2] = -2.0 * zNear;
result[0][0] = (2.0f * z_near) / (right - left);
result[1][1] = (2.0f * z_near) / (top - bottom);
result[2][2] = 1.0f;
result[2][3] = 1.0f;
result[3][2] = -2.0 * z_near;
return result;
}
@ -25,7 +42,7 @@ Matrix4x4 transform::orthographic(float left, float right, float bottom, float t
Matrix4x4 result(1.0f);
result[0][0] = 2.0f / (right - left);
result[1][1] = 2.0f / (top - bottom);
result[2][2] = - 1.0f / (zFar - zNear);
result[2][2] = 1.0f / (zFar - zNear);
result[3][0] = -(right + left) / (right - left);
result[3][1] = -(top + bottom) / (top - bottom);
result[3][2] = - zNear / (zFar - zNear);
@ -36,7 +53,7 @@ Matrix4x4 transform::orthographic(float left, float right, float bottom, float t
Matrix4x4 transform::look_at(const Vector3 eye, const Vector3 center, const Vector3 up) {
const Vector3 f = normalize(center - eye);
const Vector3 s = normalize(cross(f, up));
const Vector3 u = cross(s, f);
const Vector3 u = cross(f, s);
Matrix4x4 result(1.0f);
result[0][0] = s.x;
@ -45,12 +62,12 @@ Matrix4x4 transform::look_at(const Vector3 eye, const Vector3 center, const Vect
result[0][1] = u.x;
result[1][1] = u.y;
result[2][1] = u.z;
result[0][2] = -f.x;
result[1][2] = -f.y;
result[2][2] = -f.z;
result[0][2] = f.x;
result[1][2] = f.y;
result[2][2] = f.z;
result[3][0] = -dot(s, eye);
result[3][1] = -dot(u, eye);
result[3][2] = dot(f, eye);
result[3][2] = -dot(f, eye);
return result;
}
@ -104,7 +121,7 @@ Quaternion transform::quat_look_at(const Vector3 eye, const Vector3 center, cons
const Vector3 direction = normalize(center - eye);
Matrix3x3 result(1.0f);
result[2] = -direction;
result[2] = direction;
result[0] = cross(up, result[2]);
result[1] = cross(result[2], result[0]);

View file

@ -165,12 +165,6 @@ public:
std::unique_ptr<ShadowPass> shadow_pass;
std::unique_ptr<SceneCapture> scene_capture;
/*
This is applied to most framebuffer pass projection-view matrices.
For example, under Metal the y is flipped because the framebuffer sample coordinates are flipped.
*/
Matrix4x4 correction_matrix;
GFXTexture* dummyTexture = nullptr;
GFXRenderPass* unormRenderPass = nullptr;
GFXPipeline* renderToUnormTexturePipeline = nullptr;

View file

@ -94,9 +94,6 @@ Renderer::Renderer(GFX* gfx, const bool enable_imgui) : gfx(gfx) {
if(enable_imgui)
addPass<ImGuiPass>();
if(gfx->required_context() == GFXContext::Metal)
correction_matrix[1][1] *= -1.0f;
createBRDF();
}
@ -242,7 +239,7 @@ void Renderer::render(Scene* scene, int index) {
for(auto& [obj, camera] : cameras) {
const int actual_width = render_extent.width / cameras.size();
camera.perspective = transform::perspective(radians(camera.fov), static_cast<float>(actual_width) / static_cast<float>(render_extent.height), camera.near);
camera.perspective = transform::infinite_perspective(radians(camera.fov), static_cast<float>(actual_width) / static_cast<float>(render_extent.height), camera.near);
camera.view = inverse(scene->get<Transform>(obj).model);
Viewport viewport = {};
@ -354,7 +351,7 @@ void Renderer::render_camera(GFXCommandBuffer* command_buffer, Scene& scene, Obj
sceneInfo.options = Vector4(1, 0, 0, 0);
sceneInfo.camPos = scene.get<Transform>(camera_object).get_world_position();
sceneInfo.camPos.w = 2.0f * camera.near * std::tanf(camera.fov * 0.5f) * (static_cast<float>(extent.width) / static_cast<float>(extent.height));
sceneInfo.vp = camera.perspective * correction_matrix * camera.view;
sceneInfo.vp = camera.perspective * camera.view;
for(const auto [obj, light] : scene.get_all<Light>()) {
SceneLight sl;
@ -472,7 +469,7 @@ void Renderer::render_camera(GFXCommandBuffer* command_buffer, Scene& scene, Obj
RenderScreenOptions options = {};
options.render_world = true;
options.mvp = camera.perspective * camera.view * correction_matrix * scene.get<Transform>(obj).model;
options.mvp = camera.perspective * camera.view * scene.get<Transform>(obj).model;
render_screen(command_buffer, screen.screen, continuity, options);
}
@ -483,7 +480,7 @@ void Renderer::render_camera(GFXCommandBuffer* command_buffer, Scene& scene, Obj
float aspect;
} pc;
pc.view = matrix_from_quat(scene.get<Transform>(camera_object).rotation) * correction_matrix;
pc.view = matrix_from_quat(scene.get<Transform>(camera_object).rotation);
pc.aspect = static_cast<float>(extent.width) / static_cast<float>(extent.height);
for(const auto& [obj, light] : scene.get_all<Light>()) {
@ -702,8 +699,6 @@ void Renderer::create_mesh_pipeline(Material& material) {
pipelineInfo.shaders.fragment_src = material_compiler.compile_material_fragment(material, false); // scene capture does not use IBL
pipelineInfo.rasterization.culling_mode = GFXCullingMode::Frontface;
material.capture_pipeline = material_compiler.create_static_pipeline(pipelineInfo, false, true);
}

View file

@ -159,7 +159,7 @@ void SceneCapture::render(GFXCommandBuffer* command_buffer, Scene* scene) {
const Vector3 lightPos = scene->get<Transform>(obj).get_world_position();
const Matrix4x4 projection = transform::perspective(radians(90.0f), 1.0f, 0.1f);
const Matrix4x4 projection = transform::infinite_perspective(radians(90.0f), 1.0f, 0.1f);
const Matrix4x4 model = transform::translate(Matrix4x4(), Vector3(-lightPos.x, -lightPos.y, -lightPos.z));
SceneInformation sceneInfo = {};
@ -294,7 +294,6 @@ void SceneCapture::render(GFXCommandBuffer* command_buffer, Scene* scene) {
} pc;
pc.view = skyTransforms[face];
pc.view[3] = Vector4(0, 0, 0, 1); // zero out translation
pc.aspect = 1.0f;
for(auto& [obj, light] : scene->get_all<Light>()) {

View file

@ -193,9 +193,11 @@ void ShadowPass::render_sun(GFXCommandBuffer* command_buffer, Scene& scene, Obje
const Matrix4x4 projection = transform::orthographic(-25.0f, 25.0f, -25.0f, 25.0f, 0.1f, 100.0f);
const Matrix4x4 view = transform::look_at(lightPos, Vector3(0), Vector3(0, 1, 0));
const Matrix4x4 realMVP = projection * engine->get_renderer()->correction_matrix * view;
const Matrix4x4 realMVP = projection * view;
scene.lightSpace = projection * view;
scene.lightSpace = projection;
scene.lightSpace[1][1] *= -1;
scene.lightSpace = scene.lightSpace * view;
const auto frustum = normalize_frustum(extract_frustum(projection * view));
@ -224,11 +226,13 @@ void ShadowPass::render_spot(GFXCommandBuffer* command_buffer, Scene& scene, Obj
command_buffer->set_viewport(viewport);
const Matrix4x4 perspective = transform::perspective(radians(90.0f), 1.0f, 0.1f);
const Matrix4x4 perspective = transform::perspective(radians(90.0f), 1.0f, 0.1f, 100.0f);
const Matrix4x4 realMVP = perspective * engine->get_renderer()->correction_matrix * inverse(scene.get<Transform>(light_object).model);
const Matrix4x4 realMVP = perspective * inverse(scene.get<Transform>(light_object).model);
scene.spotLightSpaces[last_spot_light] = perspective * inverse(scene.get<Transform>(light_object).model);
scene.spotLightSpaces[last_spot_light] = perspective;
scene.spotLightSpaces[last_spot_light][1][1] *= -1;
scene.spotLightSpaces[last_spot_light] = scene.spotLightSpaces[last_spot_light] * inverse(scene.get<Transform>(light_object).model);
const auto frustum = normalize_frustum(extract_frustum(perspective * inverse(scene.get<Transform>(light_object).model)));
@ -267,12 +271,12 @@ void ShadowPass::render_point(GFXCommandBuffer* command_buffer, Scene& scene, Ob
const Vector3 lightPos = scene.get<Transform>(light_object).get_world_position();
const Matrix4x4 projection = transform::perspective(radians(90.0f), 1.0f, 0.1f);
const Matrix4x4 model = transform::translate(Matrix4x4(), Vector3(-lightPos.x, -lightPos.y, -lightPos.z));
const Matrix4x4 projection = transform::perspective(radians(90.0f), 1.0f, 0.1f, 100.0f);
const Matrix4x4 model = inverse(scene.get<Transform>(light_object).model);
const auto frustum = normalize_frustum(extract_frustum(projection * shadowTransforms[face] * model));
render_meshes(command_buffer, scene, projection * engine->get_renderer()->correction_matrix * shadowTransforms[face], model, lightPos, Light::Type::Point, frustum);
render_meshes(command_buffer, scene, projection * shadowTransforms[face], model, lightPos, Light::Type::Point, frustum);
command_buffer->copy_texture(offscreen_color_texture, render_options.shadow_resolution, render_options.shadow_resolution, scene.pointLightArray, face, last_point_light, 0);
}
@ -315,8 +319,7 @@ void ShadowPass::create_pipelines() {
pipelineInfo.render_pass = render_pass;
pipelineInfo.depth.depth_mode = GFXDepthMode::LessOrEqual;
pipelineInfo.rasterization.culling_mode = GFXCullingMode::None;
// sun
{
pipelineInfo.label = "Sun Shadow";

View file

@ -31,7 +31,6 @@ void SMAAPass::render(GFXCommandBuffer* command_buffer) {
} pc;
pc.viewport = Vector4(1.0f / static_cast<float>(extent.width), 1.0f / static_cast<float>(extent.height), extent.width, extent.height);
pc.correction_matrix = engine->get_renderer()->correction_matrix;
// edge
{

View file

@ -20,6 +20,7 @@ layout(location = 1) out vec4 outOffset;
void main() {
outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
gl_Position = vec4(outUV * 2.0f + -1.0f, 0.0f, 1.0f);
outUV.y = 1.0 - outUV.y;
SMAANeighborhoodBlendingVS(outUV, outOffset);
}

View file

@ -110,7 +110,7 @@ float pcss_sun(const vec4 shadowCoords, float light_size_uv) {
if(num_blockers < 1)
return 1.0;
const float penumbraWidth = penumbra_size(shadowCoords.z, average_blocker_depth);
const float penumbraWidth = penumbra_size(-shadowCoords.z, average_blocker_depth);
const float uvRadius = penumbraWidth * light_size_uv * 0.1 / shadowCoords.z;
return pcf_sun(shadowCoords, uvRadius);
@ -163,7 +163,7 @@ void blocker_distance_spot(const vec3 shadowCoords, const int index, const float
for(int i = 0; i < numBlockerSearchSamples; i++) {
const float z = texture(sampler2DArray(spot_shadow, shadow_sampler),
vec3(shadowCoords.xy + PoissonOffsets[i] * searchWidth, index)).r;
if(z > shadowCoords.z) {
if(z < shadowCoords.z) {
blockerSum += z;
blockers++;
}
@ -179,7 +179,7 @@ float pcss_spot(const vec4 shadowCoords, const int index, float light_size_uv) {
if(num_blockers < 1)
return 1.0;
const float penumbraWidth = penumbra_size(shadowCoords.z, average_blocker_depth);
const float penumbraWidth = penumbra_size(-shadowCoords.z, average_blocker_depth);
const float uvRadius = penumbraWidth * light_size_uv * 0.1 / shadowCoords.z;
return pcf_spot(shadowCoords, index, uvRadius);
@ -196,18 +196,16 @@ ComputedLightInformation calculate_spot(Light light) {
float shadow = 1.0;
if(light.shadowsEnable.x == 1.0) {
const vec4 shadowCoord = fragPostSpotLightSpace[last_spot_light] / fragPostSpotLightSpace[last_spot_light].w;
if(shadowCoord.z > -1.0 && shadowCoord.z < 1.0) {
#ifdef SHADOW_FILTER_NONE
shadow = (texture(sampler2DArray(spot_shadow, shadow_sampler), vec3(shadowCoord.xy, last_spot_light)).r < shadowCoord.z) ? 0.0 : 1.0;
#endif
#ifdef SHADOW_FILTER_PCF
shadow = pcf_spot(shadowCoord, last_spot_light, 0.1);
shadow = pcf_spot(shadowCoord, last_spot_light, 0.01);
#endif
#ifdef SHADOW_FILTER_PCSS
shadow = pcss_spot(shadowCoord, last_spot_light, light.shadowsEnable.y);
#endif
}
last_spot_light++;
}
@ -215,7 +213,7 @@ ComputedLightInformation calculate_spot(Light light) {
const float inner_cutoff = light.colorSize.w + radians(5);
const float outer_cutoff = light.colorSize.w;
const float theta = dot(light_info.direction, normalize(-light.directionPower.xyz));
const float theta = dot(light_info.direction, normalize(light.directionPower.xyz));
const float epsilon = inner_cutoff - outer_cutoff;
const float intensity = clamp((theta - outer_cutoff) / epsilon, 0.0, 1.0);
@ -263,7 +261,7 @@ float pcss_point(const vec3 shadowCoords, const int index, float light_size_uv)
return 1.0;
const float depth = length(shadowCoords);
const float penumbraWidth = penumbra_size(depth, average_blocker_depth);
const float penumbraWidth = penumbra_size(-depth, average_blocker_depth);
const float uvRadius = penumbraWidth * light_size_uv;
return pcf_point(shadowCoords, index, uvRadius);

View file

@ -14,7 +14,7 @@ vec3 sky_ray(const vec2 uv) {
const float d = 0.5 / tan(sun_position_fov.w / 2.0);
return normalize(vec3((uv.x - 0.5) * aspect,
uv.y - 0.5,
-d));
d));
}
void main() {

View file

@ -131,8 +131,8 @@ void CommonEditor::update(float deltaTime) {
platform::capture_mouse(willCaptureMouse);
if(willCaptureMouse) {
yaw += engine->get_input()->get_value("lookX") * -50.0f * deltaTime;
pitch += engine->get_input()->get_value("lookY") * -50.0f * deltaTime;
yaw += engine->get_input()->get_value("lookX") * 50.0f * deltaTime;
pitch += engine->get_input()->get_value("lookY") * 50.0f * deltaTime;
const float speed = 7.00f;
@ -146,7 +146,7 @@ void CommonEditor::update(float deltaTime) {
auto [obj, cam] = engine->get_scene()->get_all<Camera>()[0];
engine->get_scene()->get<Transform>(obj).position += right * movX * speed * deltaTime;
engine->get_scene()->get<Transform>(obj).position += forward * movY * speed * deltaTime;
engine->get_scene()->get<Transform>(obj).position += forward * -movY * speed * deltaTime;
engine->get_scene()->get<Transform>(obj).rotation = angle_axis(yaw, Vector3(0, 1, 0)) * angle_axis(pitch, Vector3(1, 0, 0));
}
@ -933,7 +933,7 @@ GFXTexture* CommonEditor::generate_common_preview(Scene& scene, const Vector3 ca
engine->update_scene(scene);
scene.get<Camera>(camera).perspective = transform::perspective(radians(45.0f), 1.0f, 0.1f);
scene.get<Camera>(camera).perspective = transform::infinite_perspective(radians(45.0f), 1.0f, 0.1f);
scene.get<Camera>(camera).view = inverse(scene.get<Transform>(camera).model);
auto renderer = engine->get_renderer();

View file

@ -236,7 +236,7 @@ void DebugPass::render_scene(Scene& scene, GFXCommandBuffer* commandBuffer) {
Vector4 color;
};
Matrix4x4 vp = camera.perspective * engine->get_renderer()->correction_matrix * camera.view;
Matrix4x4 vp = camera.perspective * camera.view;
commandBuffer->set_pipeline(primitive_pipeline);
@ -519,7 +519,7 @@ void DebugPass::get_selected_object(int x, int y, std::function<void(SelectableO
Vector4 color;
} pc;
pc.mvp = camera.perspective * engine->get_renderer()->correction_matrix * camera.view * model;
pc.mvp = camera.perspective * camera.view * model;
if(object.render_type == SelectableObject::RenderType::Sphere)
pc.mvp = transform::scale(pc.mvp, Vector3(object.sphere_size));
@ -541,17 +541,17 @@ void DebugPass::get_selected_object(int x, int y, std::function<void(SelectableO
engine->get_gfx()->copy_texture(selectTexture, selectBuffer);
uint8_t* map = reinterpret_cast<uint8_t*>(engine->get_gfx()->get_buffer_contents(selectBuffer));
uint8_t* mapped_texture = reinterpret_cast<uint8_t*>(engine->get_gfx()->get_buffer_contents(selectBuffer));
int buf_pos = 4 * ((extent.height - y) * extent.width + x); // flipped for metal
const int buffer_position = 4 * (y * extent.width + x);
uint8_t a = map[buf_pos + 3];
uint8_t a = mapped_texture[buffer_position + 3];
int id = map[buf_pos] +
map[buf_pos + 1] * 256 +
map[buf_pos + 2] * 256*256;
const int id = mapped_texture[buffer_position] +
mapped_texture[buffer_position + 1] * 256 +
mapped_texture[buffer_position + 2] * 256*256;
engine->get_gfx()->release_buffer_contents(selectBuffer, map);
engine->get_gfx()->release_buffer_contents(selectBuffer, mapped_texture);
if(a != 0) {
callback(selectable_objects[id]);