Rendering to multiple color textures? #611
-
In https://lovr.org/docs/PassType it says "A render pass renders graphics to a set of up to four color textures and an optional depth texture. " Are there any examples that demonstrate writing to multiple color textures in a single pass? I've got as far as:
and shader code like:
but can't find examples of writing to these. |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 2 replies
-
Here's a minimal example demonstrating how to do it: function lovr.load()
t1 = lovr.graphics.newTexture(1, 1)
t2 = lovr.graphics.newTexture(1, 1)
t3 = lovr.graphics.newTexture(1, 1)
shader = lovr.graphics.newShader('fill', [[
layout(location = 1) out vec4 SecondColor;
layout(location = 2) out vec4 ThirdColor;
vec4 lovrmain() {
SecondColor = vec4(0, 1, 0, 1);
ThirdColor = vec4(0, 0, 1, 1);
return vec4(1, 0, 0, 1);
}
]])
end
function lovr.draw(pass)
local gbuffer = lovr.graphics.getPass('render', { t1, t2, t3, samples = 1, depth = false })
gbuffer:setShader(shader)
gbuffer:fill()
pass:setMaterial(t1)
pass:plane(-1, 2, -3)
pass:setMaterial(t2)
pass:plane(0, 2, -3)
pass:setMaterial(t3)
pass:plane(1, 2, -3)
return lovr.graphics.submit(gbuffer, pass)
end I found a couple of minor bugs in testing this (MSAA doesn't work, blend mode can only be set for 1st target, the shader API for it wasn't quite working as intended and requires manually declaring It would be great to flesh this out into a deferred rendering example at some point. |
Beta Was this translation helpful? Give feedback.
-
Thanks, this works! May have found a related bug/issue. If you remove the
|
Beta Was this translation helpful? Give feedback.
-
Thanks for your help @bjornbytes. I've hacked up a crude SSAO example, which you're welcome to reuse/adapt for the lovr docs/website. For my purposes at the moment it's good enough! -- Demonstrates a basic ambient occlusion shader, by Ben Porter 2022
---@diagnostic disable: lowercase-global, duplicate-set-field
local show_render_textures = false -- Set to true to display the position and normal textures
function create_gbuffer_shader()
local vs = [[
vec4 lovrmain()
{
return Projection * View * Transform * VertexPosition;
}
]]
local fs = [[
layout(location = 1) out vec4 OutPosition;
vec4 lovrmain()
{
OutPosition = vec4(PositionWorld, 1);
return vec4(normalize(Normal), 1);
}
]]
return lovr.graphics.newShader(vs, fs, {})
end
function create_lighting_shader()
local fs = [[
layout(set = 2, binding = 0) uniform texture2D normalTexture;
layout(set = 2, binding = 1) uniform texture2D positionTexture;
vec4 lovrmain()
{
const int kernelSamples = 32;
const float kernelSpan = 16;
const float minDist = 0.1;
const float maxDist = 1.5;
const float minDot = 0.1;
const float kernelSampleMultiplier = 1.0 / (kernelSamples*kernelSamples);
const float occlusionMultiplier = 1.5; // Increase to expand darks
const float occlusionExponent = 1.2; // Increase to expand lights
const float ambienceMultiplier = 0.6;
const float diffuseMultiplier = 0.4;
const vec4 ambientColour = vec4(.6,.6,1,1);
const vec4 diffuseColour = vec4(1,1,.7,1);
vec4 position = getPixel(positionTexture, UV);
if (position.a == 0){
discard;
}
vec4 normal = getPixel(normalTexture, UV);
float occlusion = 0.0;
for(int i = 0; i < kernelSamples; ++i)
{
for(int j = 0; j < kernelSamples; ++j)
{
float di = kernelSpan*(i - kernelSamples/2) / (Resolution.x*kernelSamples/2);
float dj = kernelSpan*(j - kernelSamples/2) / (Resolution.y*kernelSamples/2);
vec4 neighbourPosition = getPixel(positionTexture, UV + vec2(di,dj));
if (neighbourPosition.a > 0){
vec3 offset = neighbourPosition.xyz - position.xyz;
float d = length(offset);
vec3 dir = offset / d;
if ( dot(dir, normal.xyz) > minDot && d>minDist && d<maxDist)
{
float sampleOcclusion = exp(-d); // simulate falloff
occlusion += sampleOcclusion * kernelSampleMultiplier;
}
}
}
}
float ambience = clamp(ambienceMultiplier * (1-occlusionMultiplier*pow(occlusion,occlusionExponent)), 0, 1);
float diffuse = diffuseMultiplier*max(0, dot(normal.xyz, vec3(0,1,0)));
return ambience * ambientColour + diffuse * diffuseColour;
}
]]
return lovr.graphics.newShader('fill', fs, {})
end
local gbuffer_shader
local normal_texture, position_texture
local lighting_shader
function lovr.load(args)
local width, height = lovr.system.getWindowWidth(), lovr.system.getWindowHeight()
normal_texture = lovr.graphics.newTexture(width, height, {linear = true, mipmaps = false})
position_texture = lovr.graphics.newTexture(width, height, {mipmaps = false, format = 'rgba16f'})
gbuffer_shader = create_gbuffer_shader()
lighting_shader = create_lighting_shader()
end
function draw_scene(pass)
local t = lovr.timer.getTime() / 6
pass:setViewPose(1, 0, 2, 4, -math.pi / 8, 1, 0, 0)
pass:rotate(lovr.math.quat(t, 0, 1, 0))
for i=-1,1 do
for j=-1,1 do
for k=-1,1 do
pass:push()
pass:translate(lovr.math.vec3(i,j,k))
pass:scale(0.45)
local geo = (i+j+k)%3
if geo == 0 then
pass:sphere()
else
pass:push()
pass:scale(1.3)
pass:rotate(lovr.math.quat(4*t + 13*i+j+k,1,1,1))
if geo == 1 then
pass:box()
else
pass:scale(0.3)
for di=-1,1 do
for dj=-1,1 do
for dk=-1,1 do
pass:push()
pass:translate(lovr.math.vec3(di*1.4,dj*1.4,dk*1.4))
pass:box()
pass:pop()
end
end
end
end
pass:pop()
end
pass:pop()
end
end
end
end
function draw_normals()
lovr.graphics.setBackgroundColor(0,0,0,0)
local normal_writer = lovr.graphics.getPass('render', { normal_texture, position_texture, samples = 1, depth = true })
normal_writer:setShader(gbuffer_shader)
draw_scene(normal_writer)
normal_writer:setShader()
return normal_writer
end
function lovr.draw(pass)
local normal_pass = draw_normals()
local width, height = lovr.system.getWindowWidth(), lovr.system.getWindowHeight()
lovr.graphics.setBackgroundColor(.3,.3,.45)
-- Render individual passes
if show_render_textures then
pass:origin()
pass:setMaterial(position_texture)
pass:setViewport(0,0,width/2,height/2)
pass:fill()
pass:setMaterial(normal_texture)
pass:setViewport(width/2,0,width/2,height/2)
pass:fill()
else
pass:setShader(lighting_shader)
pass:send('normalTexture', normal_texture)
pass:send('positionTexture', position_texture)
pass:fill()
end
return lovr.graphics.submit(normal_pass, pass)
end |
Beta Was this translation helpful? Give feedback.
Here's a minimal example demonstrating how to do it: