blend mode (?) issue / canvas rendering problem

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
milo_goat
Prole
Posts: 5
Joined: Sun Nov 12, 2023 3:47 pm

blend mode (?) issue / canvas rendering problem

Post by milo_goat »

a project im working on is extremely graphics involved so ive learned a lot about the love api, but just now ive encountered a problem I feel like shouldnt be a problem; I have a scene going to be on the window, made up of many small aspects, some polygons, some images, some canvases. but before it is drawn, a series of shaders are applied so (I think) everything becomes part of one, screen sized canvas draw to the window, but afterwards I want to apply another canvas that is black and white (all pixels are : (n, n, n, 1)) to add shadows, so id assume I could use setBlendMode('multiply', 'premultiplied') and draw the canvas but when I do it turns the screen black.

window without blendmode or shadow canvas drawn: (its just a scene from the game)
Image

window with the shadows drawn but blendMode is still 'alpha' :
Image

window with the shadow draw and blandMode set to 'multiplied, 'premultiplied' : (just a black screen)
Image

the shadow is claculated with polygons and is drawn to a canvas, the canvas is then drawn to another canvas (combine all lights to one screen), then this canvas is draw atop the already rendered window after everything has been draw.

love.draw function() looks like the following:

simplified version :

Code: Select all

function love.draw()
	walls:sendDrawToRend()
	entities:sendDrawToRend()
	player:sendDrawToRend()
	UI:sendDrawToRend()
	-- all viewable items are sent to one script to be drawn with shaders
	
	betterRender:drawAll()
	--combine all drawables into a canvases, apply shaders, and draw to screen
	
	stats:draw()
	--draw debug info (cpu time, gpu time, texture memory on screen)
	
	geometryShadows:drawLights()
	--draw shadows (problem)
end
real version :

Code: Select all

function love.draw()
    local startTime = love.timer.getTime()

    Walls:draw()

    BetterEntity:draw()
    Player:draw()
    UserInterface:draw()
    Camera:draw()

    Blood:update(dt)
    Gore:draw()
    AnimatedNonentity:draw()

    ps3d:draw('foreGround')

    if paused then
        Settings:draw()
    end

    BetterRender:draw()

    local poses = Camera:getPos()
    for i = 1, #splines do
        splines[i]:draw(poses.x, poses.y, poses.zoom)
    end

    if debugmode then
        love.graphics.print('cpu run time. ms : ' .. math.floor((cpuTime) * 1000000) / 1000, 30, 0)
        love.graphics.print('gpu run time. ms : ' .. math.floor((gpuTime) * 1000000) / 1000, 30, 20)
        love.graphics.print('total run time. ms : ' .. math.floor((totalTime) * 1000000) / 1000, 30, 40)

        love.graphics.print(string.format("%.2f MB texture memory", love.graphics.getStats().texturememory / 1000 / 1000), 30, 60)
        love.graphics.print('FPS : ' .. love.timer.getFPS(), CommonVars.screenWidth - 250, 0)

        for i = 1, #totalTimeGraph - 1 do
            love.graphics.line(350 + i, 60 - totalTimeGraph[i], 350 + (i + 1), 60 - totalTimeGraph[i + 1])
        end
        
        love.graphics.print('screen refresh : ' .. screenItems.refreshrate, CommonVars.screenWidth - 250, 20)
    end

    love.graphics.setBlendMode("alpha")

    BetterGeometryShadow:drawLights()
    -- draw lights and shadows

    gpuTime = love.timer.getTime() - startTime
    totalTime = love.timer.getTime() - totalStart
    table.insert(totalTimeGraph, totalTime * 1000000 / 1000)
    table.remove(totalTimeGraph, 1)

    local curTime = love.timer.getTime()
    if nextTime <= curTime then
        nextTime = curTime
        return
    end
    love.timer.sleep(nextTime - curTime)
end
and the geometryShadows:drawLights() function looks like this :

simplified version :

Code: Select all

function geometryShadows:drawLights()
	love.graphics.setShader(inverseSquareRootShader)
	for i = 1, #allLights do
		allLights[i]:drawAsLight() -- each light calculates shadows and draws them to its canvas
	end
	
	love.graphics.setCanvas(finalCanvas)
	love.graphics.setShader()
	
	for i = 1, #allLights do
		love.graphics.draw(allLights[i].canvas) -- draw lights canvases to one canvas to draw on screen
	end
	
	love.graphics.setCanvas()
   	love.graphics.setBlendMode("multiply", "premultiplied") -- blend mode
    	love.graphics.draw(finalCanv) -- draw shadows
end
real version :

Code: Select all

function BetterGeometryShadow:drawLights()
    love.graphics.setShader(BetterShaders:getShader("betterGeometryShadowInvSqroot"))
    love.graphics.setBlendMode('replace')
    for i = 1, #allLights do
        allLights[i]:drawAsLight()
    end

    love.graphics.setCanvas(finalCanv)
    love.graphics.clear()

    love.graphics.setColor(1, 1, 1, 1)
    love.graphics.setBlendMode('alpha')

    love.graphics.setShader()

    local camPos = Camera:getPos()

    for i = 1, #allLights do
        love.graphics.draw(allLights[i].canv, -allLights[i].power + (allLights[i].x - camPos.x + CommonVars.screenWidth / camPos.zoom - CommonVars.screenWidth) * camPos.zoom, -allLights[i].power + (allLights[i].y - camPos.y + CommonVars.screenHeight / camPos.zoom - CommonVars.screenHeight) * camPos.zoom, 0, camPos.zoom, camPos.zoom)
    end

    love.graphics.setCanvas()
    love.graphics.setBlendMode("multiply", "premultiplied")
    love.graphics.draw(finalCanv)
end
this is the second itteration of the shadows script, the first one worked properly and here was the order of drawing for that one:

Code: Select all

function love.draw()
	walls:sendDrawToRend()
	entities:sendDrawToRend()
	player:sendDrawToRend()
	UI:sendDrawToRend()
	-- all viewable items are sent to one script to be drawn with shaders
	
	betterRender:drawAll()
	--combine all drawables into a canvases, apply shaders, and draw to screen
	
	local shadows = GeometryShadow:drawShadows() -- returns the fincal canvas
    	love.graphics.setShader(BetterShaders:getShader("geometryShadow")
    	 -- set shader to a root function
    	 -- lights were adding 0.1 to pixels they touched and this would make the lights additive but non-linear
    	 -- shader turned lights from a / shaped graph 0-1 into a r shape graph 0-1
    	 
    	love.graphics.setBlendMode("multiply", "premultiplied") -- set blend mode
    	love.graphics.setCanvas() -- draw to window
    	love.graphics.draw(shadows) -- draw shadow canvas to canvas
    	
    	stats:draw()
end
no, rendering it before stats:draw() doesnt change the outcome, I saw that difference between the scripts.

and I would like this to be an alteration of code (hopefully I just made a mistake somewhere in the drawing cast), making a new shader and redrawing the current screen and giving the shadow canvas as an extern Image may be a solution but I'm in testing phase for the feature so altering the betterRender:drawAll() code to have this would be more effort than its worth and would make it less efficient at larger drawing scales with many lights since it'd have to be before the ui is drawn so it would be applied to more than one canvas (just the way its worked out so far sadly)

here is code for individual lights making canvases before they are drawn to finalCanvas that is then to the screen:

simplified version :

Code: Select all

function geometryShadow:drawAsLight()
	--shader is already inverse square root shader (was set before function is run)
    	love.graphics.setCanvas(self.canv)
   	love.graphics.clear(0, 0, 0, 1)
    
    	love.graphics.polygon('fill', self.lightVertices)
end
real version :

Code: Select all

function BetterGeometryShadow:drawAsLight()
	-- shader was set to the inverse square root shader before this function was called
    	love.graphics.push()
    	love.graphics.setCanvas(self.canv) -- draw to its own canvas
    	love.graphics.clear(0, 0, 0, 1)
    	-- set screen to black
    
    	love.graphics.translate(-self.x + self.power, -self.y + self.power)
    	-- draw in the center of the canvas
	for i = 2, #self.shadowVertices - 1, 2 do
        	local p1 = {x = self.shadowVertices[i].x, y = self.shadowVertices[i].y}
        	local p2 = {x = self.shadowVertices[i + 1].x, y = self.shadowVertices[i + 1].y}
        
        	if p1.x ~= p2.x or p1.y ~= p2.y then
        		love.graphics.polygon('fill', {p1.x, p1.y, p2.x, p2.y, self.x, self.y})
        	end
	end
	-- draws a triangle fan from the center of the light to all visible areas (draws the light not the shadow)

    	local p1 = {x = self.shadowVertices[#self.shadowVertices].x, y = self.shadowVertices[#self.shadowVertices].y}
    	local p2 = {x = self.shadowVertices[1].x, y = self.shadowVertices[1].y}
    	-- gets last and first point in the triangle fan (rendering issue fix)

    	if (math.atan2(p2.y - self.y, p2.x - self.x) - math.atan2(p1.y - self.y, p1.x - self.x) + math.pi) % (math.pi * 2) > math.pi then
    		love.graphics.polygon('fill', {p1.x, p1.y, p2.x, p2.y, self.x, self.y})
    	end
    	-- draws triangle between the last and first vertice only if it wont draw over the other triangles (defunct because of faux polygon at edge of light distance)
    	

   	love.graphics.pop()
end
Attachments
image of shadow being drawn and blend mode being set (black screen)
image of shadow being drawn and blend mode being set (black screen)
drawAndBlend.png (3.64 KiB) Viewed 7647 times
image of shadow being drawn but blend mode not being changed
image of shadow being drawn but blend mode not being changed
drawNoBlend.png (67.22 KiB) Viewed 7647 times
image of no blend mode or shadow
image of no blend mode or shadow
noDrawnoBlend.png (42.75 KiB) Viewed 7647 times
milo_goat
Prole
Posts: 5
Joined: Sun Nov 12, 2023 3:47 pm

Re: blend mode (?) issue / canvas rendering problem

Post by milo_goat »

lol I just forgot to set blend mode back to alpha, fml
Post Reply

Who is online

Users browsing this forum: No registered users and 45 guests