mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-16 22:30:42 +02:00
Modified outline shader to do fill branch on CPU. Fixed assymetric outline
due to depth sample not offset by a half texel in shader.
This commit is contained in:
parent
b9eb317d9b
commit
b43514fb8b
5 changed files with 162 additions and 121 deletions
|
@ -1,8 +1,7 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
//
|
||||
// Outline.slf
|
||||
// Add outline effect based on two zbuffers : one containing the total scene z and another
|
||||
// with the z of only the objects to be outlined
|
||||
// with the z of only the objects to be outlined.
|
||||
// This is the version without the fill effect inside the silhouette.
|
||||
//
|
||||
// Created by Olivier Prat on 08/09/2017
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
|
@ -10,81 +9,5 @@
|
|||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
<@include DeferredTransform.slh@>
|
||||
<$declareDeferredFrameTransform()$>
|
||||
|
||||
<@include Outline_shared.slh@>
|
||||
|
||||
uniform outlineParamsBuffer {
|
||||
OutlineParameters params;
|
||||
};
|
||||
|
||||
uniform sampler2D sceneDepthMap;
|
||||
uniform sampler2D outlinedDepthMap;
|
||||
|
||||
in vec2 varTexCoord0;
|
||||
out vec4 outFragColor;
|
||||
|
||||
const float FAR_Z = 1.0;
|
||||
const float LINEAR_DEPTH_BIAS = 5e-3;
|
||||
const float OPACITY_EPSILON = 5e-3;
|
||||
|
||||
void main(void) {
|
||||
float outlinedDepth = texture(outlinedDepthMap, varTexCoord0).x;
|
||||
float intensity = 0.0;
|
||||
|
||||
if (outlinedDepth < FAR_Z) {
|
||||
// We're not on the far plane so we are on the outlined object, thus no outline to do!
|
||||
|
||||
// But maybe we need to fill the interior
|
||||
// TODO: this should be done as another shader with switch done on CPU
|
||||
if (params._fillOpacityUnoccluded>OPACITY_EPSILON && params._fillOpacityUnoccluded>OPACITY_EPSILON) {
|
||||
float sceneDepth = texture(sceneDepthMap, varTexCoord0).x;
|
||||
// Transform to linear depth for better precision
|
||||
outlinedDepth = -evalZeyeFromZdb(outlinedDepth);
|
||||
sceneDepth = -evalZeyeFromZdb(sceneDepth);
|
||||
|
||||
// Are we occluded?
|
||||
if (sceneDepth < (outlinedDepth-LINEAR_DEPTH_BIAS)) {
|
||||
intensity = params._fillOpacityOccluded;
|
||||
} else {
|
||||
intensity = params._fillOpacityUnoccluded;
|
||||
}
|
||||
} else {
|
||||
discard;
|
||||
}
|
||||
} else {
|
||||
float weight = 0.0;
|
||||
vec2 deltaUv = params._size / params._blurKernelSize;
|
||||
vec2 lineStartUv = varTexCoord0 - params._size / 2.0;
|
||||
vec2 uv;
|
||||
int x;
|
||||
int y;
|
||||
|
||||
for (y=0 ; y<params._blurKernelSize ; y++) {
|
||||
uv = lineStartUv;
|
||||
lineStartUv.y += deltaUv.y;
|
||||
|
||||
if (uv.y>=0.0 && uv.y<=1.0) {
|
||||
for (x=0 ; x<params._blurKernelSize ; x++) {
|
||||
if (uv.x>=0.0 && uv.x<=1.0)
|
||||
{
|
||||
outlinedDepth = texture(outlinedDepthMap, uv).x;
|
||||
intensity += (outlinedDepth < FAR_Z) ? 1.0 : 0.0;
|
||||
weight += 1.f;
|
||||
}
|
||||
uv.x += deltaUv.x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
intensity /= weight;
|
||||
if (intensity < OPACITY_EPSILON) {
|
||||
discard;
|
||||
}
|
||||
|
||||
intensity = min(1.0, intensity / params._threshold) * params._intensity;
|
||||
}
|
||||
|
||||
outFragColor = vec4(params._color.rgb, intensity);
|
||||
}
|
||||
<@include Outline.slh@>
|
||||
<$main(0)$>
|
||||
|
|
95
libraries/render-utils/src/Outline.slh
Normal file
95
libraries/render-utils/src/Outline.slh
Normal file
|
@ -0,0 +1,95 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
<!
|
||||
// Outline.slh
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Olivier Prat on 9/7/17.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
!>
|
||||
<@include DeferredTransform.slh@>
|
||||
<$declareDeferredFrameTransform()$>
|
||||
|
||||
<@include Outline_shared.slh@>
|
||||
|
||||
uniform outlineParamsBuffer {
|
||||
OutlineParameters params;
|
||||
};
|
||||
|
||||
uniform sampler2D sceneDepthMap;
|
||||
uniform sampler2D outlinedDepthMap;
|
||||
|
||||
in vec2 varTexCoord0;
|
||||
out vec4 outFragColor;
|
||||
|
||||
const float FAR_Z = 1.0;
|
||||
const float LINEAR_DEPTH_BIAS = 5e-3;
|
||||
const float OPACITY_EPSILON = 5e-3;
|
||||
|
||||
<@func main(IS_FILLED)@>
|
||||
|
||||
void main(void) {
|
||||
float outlinedDepth = texture(outlinedDepthMap, varTexCoord0).x;
|
||||
float intensity = 0.0;
|
||||
|
||||
if (outlinedDepth < FAR_Z) {
|
||||
// We're not on the far plane so we are on the outlined object, thus no outline to do!
|
||||
<@if IS_FILLED@>
|
||||
// But we need to fill the interior
|
||||
float sceneDepth = texture(sceneDepthMap, varTexCoord0).x;
|
||||
// Transform to linear depth for better precision
|
||||
outlinedDepth = -evalZeyeFromZdb(outlinedDepth);
|
||||
sceneDepth = -evalZeyeFromZdb(sceneDepth);
|
||||
|
||||
// Are we occluded?
|
||||
if (sceneDepth < (outlinedDepth-LINEAR_DEPTH_BIAS)) {
|
||||
intensity = params._fillOpacityOccluded;
|
||||
} else {
|
||||
intensity = params._fillOpacityUnoccluded;
|
||||
}
|
||||
<@else@>
|
||||
discard;
|
||||
<@endif@>
|
||||
} else {
|
||||
float weight = 0.0;
|
||||
vec2 deltaUv = params._size / params._blurKernelSize;
|
||||
// We offset by half a texel to be centered on the depth sample. If we don't do this
|
||||
// the blur will have a different width between the left / right sides and top / bottom
|
||||
// sides of the silhouette
|
||||
vec2 lineStartUv = varTexCoord0 + (getInvWidthHeight() - params._size) / 2.0;
|
||||
vec2 uv;
|
||||
int x;
|
||||
int y;
|
||||
|
||||
for (y=0 ; y<params._blurKernelSize ; y++) {
|
||||
uv = lineStartUv;
|
||||
lineStartUv.y += deltaUv.y;
|
||||
|
||||
if (uv.y>=0.0 && uv.y<=1.0) {
|
||||
for (x=0 ; x<params._blurKernelSize ; x++) {
|
||||
if (uv.x>=0.0 && uv.x<=1.0)
|
||||
{
|
||||
outlinedDepth = texture(outlinedDepthMap, uv).x;
|
||||
intensity += (outlinedDepth < FAR_Z) ? 1.0 : 0.0;
|
||||
weight += 1.f;
|
||||
}
|
||||
uv.x += deltaUv.x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
intensity /= weight;
|
||||
if (intensity < OPACITY_EPSILON) {
|
||||
discard;
|
||||
}
|
||||
|
||||
intensity = min(1.0, intensity / params._threshold) * params._intensity;
|
||||
}
|
||||
|
||||
outFragColor = vec4(params._color.rgb, intensity);
|
||||
}
|
||||
|
||||
<@endfunc@>
|
|
@ -22,6 +22,7 @@
|
|||
#include "debug_deferred_buffer_vert.h"
|
||||
#include "debug_deferred_buffer_frag.h"
|
||||
#include "Outline_frag.h"
|
||||
#include "Outline_filled_frag.h"
|
||||
|
||||
using namespace render;
|
||||
|
||||
|
@ -153,7 +154,6 @@ void DrawOutline::run(const render::RenderContextPointer& renderContext, const I
|
|||
const auto frameTransform = inputs.get0();
|
||||
auto outlinedDepthBuffer = mainFrameBuffer->getPrimaryDepthTexture();
|
||||
auto destinationFrameBuffer = inputs.get3();
|
||||
auto pipeline = getPipeline();
|
||||
auto framebufferSize = glm::ivec2(outlinedDepthBuffer->getDimensions());
|
||||
|
||||
if (!_primaryWithoutDepthBuffer || framebufferSize!=_frameBufferSize) {
|
||||
|
@ -164,6 +164,8 @@ void DrawOutline::run(const render::RenderContextPointer& renderContext, const I
|
|||
}
|
||||
|
||||
if (sceneDepthBuffer) {
|
||||
const auto OPACITY_EPSILON = 5e-3f;
|
||||
auto pipeline = getPipeline(_fillOpacityUnoccluded>OPACITY_EPSILON || _fillOpacityOccluded>OPACITY_EPSILON);
|
||||
auto args = renderContext->args;
|
||||
{
|
||||
auto& configuration = _configuration.edit();
|
||||
|
@ -200,7 +202,7 @@ void DrawOutline::run(const render::RenderContextPointer& renderContext, const I
|
|||
}
|
||||
}
|
||||
|
||||
const gpu::PipelinePointer& DrawOutline::getPipeline() {
|
||||
const gpu::PipelinePointer& DrawOutline::getPipeline(bool isFilled) {
|
||||
if (!_pipeline) {
|
||||
auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS();
|
||||
auto ps = gpu::Shader::createPixel(std::string(Outline_frag));
|
||||
|
@ -217,8 +219,13 @@ const gpu::PipelinePointer& DrawOutline::getPipeline() {
|
|||
state->setDepthTest(gpu::State::DepthTest(false, false));
|
||||
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
|
||||
_pipeline = gpu::Pipeline::create(program, state);
|
||||
|
||||
ps = gpu::Shader::createPixel(std::string(Outline_filled_frag));
|
||||
program = gpu::Shader::createProgram(vs, ps);
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
_pipelineFilled = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
return _pipeline;
|
||||
return isFilled ? _pipelineFilled : _pipeline;
|
||||
}
|
||||
|
||||
DebugOutline::DebugOutline() {
|
||||
|
@ -310,51 +317,53 @@ void DrawOutlineDepth::run(const render::RenderContextPointer& renderContext, co
|
|||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
|
||||
RenderArgs* args = renderContext->args;
|
||||
ShapeKey::Builder defaultKeyBuilder;
|
||||
if (!inShapes.empty()) {
|
||||
RenderArgs* args = renderContext->args;
|
||||
ShapeKey::Builder defaultKeyBuilder;
|
||||
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
args->_batch = &batch;
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
args->_batch = &batch;
|
||||
|
||||
// Setup camera, projection and viewport for all items
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
batch.setStateScissorRect(args->_viewport);
|
||||
// Setup camera, projection and viewport for all items
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
batch.setStateScissorRect(args->_viewport);
|
||||
|
||||
glm::mat4 projMat;
|
||||
Transform viewMat;
|
||||
args->getViewFrustum().evalProjectionMatrix(projMat);
|
||||
args->getViewFrustum().evalViewTransform(viewMat);
|
||||
glm::mat4 projMat;
|
||||
Transform viewMat;
|
||||
args->getViewFrustum().evalProjectionMatrix(projMat);
|
||||
args->getViewFrustum().evalViewTransform(viewMat);
|
||||
|
||||
batch.setProjectionTransform(projMat);
|
||||
batch.setViewTransform(viewMat);
|
||||
batch.setProjectionTransform(projMat);
|
||||
batch.setViewTransform(viewMat);
|
||||
|
||||
auto shadowPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder);
|
||||
auto shadowSkinnedPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withSkinned());
|
||||
auto shadowPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder);
|
||||
auto shadowSkinnedPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withSkinned());
|
||||
|
||||
std::vector<ShapeKey> skinnedShapeKeys{};
|
||||
std::vector<ShapeKey> skinnedShapeKeys{};
|
||||
|
||||
// Iterate through all inShapes and render the unskinned
|
||||
args->_shapePipeline = shadowPipeline;
|
||||
batch.setPipeline(shadowPipeline->pipeline);
|
||||
for (auto items : inShapes) {
|
||||
if (items.first.isSkinned()) {
|
||||
skinnedShapeKeys.push_back(items.first);
|
||||
// Iterate through all inShapes and render the unskinned
|
||||
args->_shapePipeline = shadowPipeline;
|
||||
batch.setPipeline(shadowPipeline->pipeline);
|
||||
for (auto items : inShapes) {
|
||||
if (items.first.isSkinned()) {
|
||||
skinnedShapeKeys.push_back(items.first);
|
||||
}
|
||||
else {
|
||||
renderItems(renderContext, items.second);
|
||||
}
|
||||
}
|
||||
else {
|
||||
renderItems(renderContext, items.second);
|
||||
|
||||
// Reiterate to render the skinned
|
||||
args->_shapePipeline = shadowSkinnedPipeline;
|
||||
batch.setPipeline(shadowSkinnedPipeline->pipeline);
|
||||
for (const auto& key : skinnedShapeKeys) {
|
||||
renderItems(renderContext, inShapes.at(key));
|
||||
}
|
||||
}
|
||||
|
||||
// Reiterate to render the skinned
|
||||
args->_shapePipeline = shadowSkinnedPipeline;
|
||||
batch.setPipeline(shadowSkinnedPipeline->pipeline);
|
||||
for (const auto& key : skinnedShapeKeys) {
|
||||
renderItems(renderContext, inShapes.at(key));
|
||||
}
|
||||
|
||||
args->_shapePipeline = nullptr;
|
||||
args->_batch = nullptr;
|
||||
});
|
||||
args->_shapePipeline = nullptr;
|
||||
args->_batch = nullptr;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
DrawOutlineTask::DrawOutlineTask() {
|
||||
|
|
|
@ -117,11 +117,12 @@ private:
|
|||
|
||||
using OutlineConfigurationBuffer = gpu::StructBuffer<OutlineParameters>;
|
||||
|
||||
const gpu::PipelinePointer& getPipeline();
|
||||
const gpu::PipelinePointer& getPipeline(bool isFilled);
|
||||
|
||||
gpu::FramebufferPointer _primaryWithoutDepthBuffer;
|
||||
glm::ivec2 _frameBufferSize {0, 0};
|
||||
gpu::PipelinePointer _pipeline;
|
||||
gpu::PipelinePointer _pipelineFilled;
|
||||
OutlineConfigurationBuffer _configuration;
|
||||
glm::vec3 _color;
|
||||
float _size;
|
||||
|
|
13
libraries/render-utils/src/Outline_filled.slf
Normal file
13
libraries/render-utils/src/Outline_filled.slf
Normal file
|
@ -0,0 +1,13 @@
|
|||
// Outline_filled.slf
|
||||
// Add outline effect based on two zbuffers : one containing the total scene z and another
|
||||
// with the z of only the objects to be outlined.
|
||||
// This is the version with the fill effect inside the silhouette.
|
||||
//
|
||||
// Created by Olivier Prat on 09/07/2017
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
<@include Outline.slh@>
|
||||
<$main(1)$>
|
Loading…
Reference in a new issue