Materials/Textures Tutorial? Rendering more than once in one frame with alpha blending

I’m trying to create a script that would render the scene more than once (let’s say, 5 times), each with a variation, and blend all these together with equal weights. Conceptually, I think this is what I’m supposed to do:

  1. Render scene at 100% alpha
  2. Modify scene, render scene at 50% alpha
  3. Modify scene, render scene at 33% alpha
  4. etc…

There are several vanilla post-processing effects that do some variation of this, and they are:
Accumulation Motion Blur (most readable with comments)
Frame Blend (crap readability)
Super DoF
There may be others as well.

The problem is, I’ve tried and failed to read these scripts. I can’t find a good explanation for how Materials and Textures work, and they are basically the entirety of how this is accomplished. I’ve tried copy-pasting some parts, hacking them together, crossing my fingers for a usable result, trying random things, but to no avail. I just don’t have enough (read: any) understanding for how Materials and Textures work, to get this script running.

Some key things:

  • This needs to work with the “poster” command, so I’m doing it in GM:RenderScene.
  • Processing time is not an issue. It can take an hour for all I care.

My current code, quite probably cringe-worthy:

[lua]if SERVER then print(“SoftPoster must be run clientside!”) return end // this is a goddamn client script!

// Frame blending code attempted to be copied from: motion_blur.lua
local mat_MotionBlur = Material( “pp/motionblur” )
local tex_MotionBlur = render.GetMoBlurTex0()
local mat_Screen = Material( “pp/fb” )

function SoftPoster(radius, resmul, split)
local i = resmul * resmul + 5 // i = number of calls of GM:RenderScene that need to be hooked, plus 5 for good measure
local up = LocalPlayer():GetAngles():Up()
local right = LocalPlayer():GetAngles():Right()
local origin = LocalPlayer():GetPos()

hook.Add("RenderScene", "SoftPoster", function()
	local alpha = 1

	LocalPlayer():SetPos(origin)
		mat_Screen:SetFloat( "$alpha", 1.0 )
		mat_Screen:SetTexture( "$basetexture", tex_MotionBlur )
		render.SetMaterial( mat_Screen )
		render.RenderView()

	LocalPlayer():SetPos(origin + (up * radius) + (right * radius))
		mat_Screen:SetFloat( "$alpha", 1.0 / 2.0 )
		mat_Screen:SetTexture( "$basetexture", tex_MotionBlur )
		render.SetMaterial( mat_Screen )
		render.RenderView()

	LocalPlayer():SetPos(origin - (up * radius) + (right * radius))
		mat_Screen:SetFloat( "$alpha", 1.0 / 3.0 )
		mat_Screen:SetTexture( "$basetexture", tex_MotionBlur )
		render.SetMaterial( mat_Screen )
		render.RenderView()

	LocalPlayer():SetPos(origin + (up * radius) - (right * radius))
		mat_Screen:SetFloat( "$alpha", 1.0 / 4.0 )
		mat_Screen:SetTexture( "$basetexture", tex_MotionBlur )
		render.SetMaterial( mat_Screen )
		render.RenderView()

	LocalPlayer():SetPos(origin - (up * radius) - (right * radius))
		mat_Screen:SetFloat( "$alpha", 1.0 / 5.0 )
		mat_Screen:SetTexture( "$basetexture", tex_MotionBlur )
		render.SetMaterial( mat_Screen )
		render.RenderView()

	LocalPlayer():SetPos(origin)

	-- render.SetMaterial( mat_MotionBlur )
	-- render.DrawScreenQuad()
	print(i)
	i = i - 1
	if (i == 0) then
		hook.Remove("RenderScene","SoftPoster")
		print("Removing hook")
	end

	return true
end)
RunConsoleCommand("poster", resmul, split)

end[/lua]

I put this code in a lua file in garrysmod/lua, and in-game I use the concommand:
lua_openscript_cl softposter.lua
I also have the binding:
bind semicolon "lua_run_cl SoftPoster(10, 2)"
for testing.

I promise the end product that I’m planning will be more elegant to use :stuck_out_tongue:

tl;dr Can you please point me at a proper explanation or tutorial for Materials and Textures?

So, after a night’s sleep and a second attempt at figuring out how Accumulated Motion Blur works, I think I have a much better understanding of how things play with each other. However, I’m still having problems. Here’s the current iteration of my code:

[lua]if SERVER then print(“SoftPoster must be run clientside!”) return end // this is a goddamn client script!

// Frame blending code copied from: motion_blur.lua
local mat_MotionBlur = Material( “pp/motionblur” )
local mat_Screen = Material( “pp/fb” )
local tex_MotionBlur = render.GetMoBlurTex0()

local function RenderWithAlpha(addalpha)
render.UpdateScreenEffectTexture()
mat_Screen:SetFloat( “$alpha”, addalpha )
local oldRT = render.GetRenderTarget()
render.SetRenderTarget(tex_MotionBlur)
render.SetMaterial(mat_Screen)
render.DrawScreenQuad()
–render.Clear(255, 0, 0, 255, true, true)
render.SetRenderTarget(oldRT)
end

function SoftPoster(radius, resmul, split)
local callsleft = resmul * resmul + 1 – + 800 – number of calls of the render hook that need to be hooked, 1 extra called pre-poster
local ent = LocalPlayer():GetEyeTrace().Entity
local up = ent:GetAngles():Up()
local right = ent:GetAngles():Right()
local origin = ent:GetPos()

print("Starting poster")
--hook.Add("RenderScene", "SoftPoster", function()
hook.Add("RenderScreenspaceEffects", "SoftPoster", function()
	mat_MotionBlur:SetFloat("$alpha", 1)
	mat_MotionBlur:SetTexture( "$basetexture", tex_MotionBlur )

	-- Center:
	ent:SetPos(origin)
		RenderWithAlpha(1)

	-- Up Right:
	ent:SetPos(origin + (up * radius) + (right * radius))
		RenderWithAlpha(1/2)

	-- Down Right:
	ent:SetPos(origin - (up * radius) + (right * radius))
		RenderWithAlpha(1/3)

	-- Up Left:
	ent:SetPos(origin + (up * radius) - (right * radius))
		RenderWithAlpha(1/4)

	-- Down Left:
	ent:SetPos(origin - (up * radius) - (right * radius))
		RenderWithAlpha(1/5)

	-- return to normal:
	ent:SetPos(origin)

	-- This part WORKS, it writes whatever was rendered in RenderWithAlpha to screen:
	render.SetMaterial( mat_MotionBlur )
	render.DrawScreenQuad()

	
	callsleft = callsleft - 1
	if (callsleft == 0) then
		hook.Remove("RenderScreenspaceEffects","SoftPoster")
		print("Removing hook")
	end

	--return true
end)
RunConsoleCommand("poster", resmul, split)

end[/lua]

The problem I’m having is one of two (I have no idea how to check which): either the entity doesn’t move inbetween calls to RenderWithAlpha even though SetPos is called, or RenderWithAlpha doesn’t work correctly.

Please help! :frowning: