Jump to content

Apply fullscreen shader after the GUI is drawn?


whiterabbit
 Share

Recommended Posts

I'm using the default grayscale posteffect shader to make everything black and white, but this doesn't affect anything drawn during Script:PostRender(context) (eg, red text is still red). Is it possible to make a shader apply after drawing takes place? Or any solution easier than manually changing the colour/textures of GUI elements - ideally more advanced shaders could also be used.

 

 

This script shows my problem. You can put this script on any entity and link it to a camera, you will see everything is greyscale except the text 'Always red'.

Script.Camera = nil --entity "Camera"
Script.Enabled = true --bool "Enabled"

function Script:Start()
self.WasEnabled = -1
end

function Script:_Enable()
self.Camera:AddPostEffect("Shaders/PostEffects/grayscale.shader")
end

function Script:_Disable()
self.Camera:ClearPostEffects()
end

function Script:UpdateWorld()
if self.Enabled~=self.WasEnabled then
if self.Enabled then
self:_Enable()
else
self._Disable()
end
end
self.WasEnabled = self.Enabled
end

function Script:PostRender(context)
context:SetBlendMode(Blend.Solid)
context:SetColor(1,0,0,1)
context:DrawText("Always red", 5, 5)
end

 

 

 

EDIT: Right after posting this I found context:SetShader, it sounds like what I want. And the forum ruined my code indentation sad.png

Link to comment
Share on other sites

I'm trying to use context:SetShader before drawing my GUI but it requires a different kind of shader I guess, if I use the same grayscale shader then the whole context becomes invisible (transparent?).

I'm really inexperienced with shaders and have only really copy and paste-editted them up to this point. I've tried modifying some of the default 'Drawing' shaders but haven't been able to get anything that just modifies the colour of what I draw to the context - I always end up with either a solid block of color, an unrelated image over the top, or nothing.

The default grayscale shader is easy to enough to understand, I just don't know why it won't work when used for context:SetShader

Link to comment
Share on other sites

The easiest way to use the 'grayscale.shader' is to just apply it to the Root of your scene via the World' Post Effects. Then just make sure the camera is setup to allow post effects either via code (if its created via code) or by selecting the 'Use Post-Effects' option in the camera properties (if its an entity in the scene browser). Either way, a post effect only effects what is rendered, whereas 2D drawing commands are applied after the rendering. Like you showed you can set the color of the 2D draw commands via 'context:SetColor()'.

 

EDIT--If you really have your heart set on applying a grayscale to your text and images via the 2D draw commands instead of just setting the color or using grayed images, then just modify the drawtext and drawimage shaders like this:

drawtext.shader's fragment main loop:

void main(void)
{
   vec4 color = drawcolor;
   color.a *= texture(texture0,ex_texcoords0).a;
   float l = color.r * 0.2126 + color.g * 0.7152 + color.b * 0.0722;
   fragData0 = vec4(l, l, l, color.a);
}

 

drawimage.shader's fragment main loop:

void main(void)
{
   vec4 color = drawcolor * texture(texture0,vTexCoords0);
   float l = color.r * 0.2126 + color.g * 0.7152 + color.b * 0.0722;
   fragData0 = vec4(l, l, l, color.a);
}

Win7 64bit / Intel i7-2600 CPU @ 3.9 GHz / 16 GB DDR3 / NVIDIA GeForce GTX 590

LE / 3DWS / BMX / Hexagon

macklebee's channel

Link to comment
Share on other sites

Ok thanks, that's easy enough. But is it not possible to do it with just one shader applied with context:SetShader before drawing all my GUI?

I'm hoping to have it be able to switch on and off. I could do it by editting both those shaders as you've said and adding a uniform value to toggle it on and off but from what I've read it's not a good idea to do boolean things like that in shader code?

 

EDIT:

For now I've gone ahead and done that - editted them and added a uniform value that will be 1 or 0 for whether greyscale is active, then used mix() to get the output.

 

//in drawimage.shader

//added a uniform
uniform int GrayscaleActive;

//changed main loop
void main(void)
{
   vec4 color = drawcolor * texture(texture0,vTexCoords0);
   float l = color.r * 0.2126 + color.g * 0.7152 + color.b * 0.0722;
   vec4 lumCol = vec4(l,l,l,color.a);
   fragData0 = mix(color,lumCol,GrayscaleActive);
}

Link to comment
Share on other sites

Ok thanks, that's easy enough. But is it not possible to do it with just one shader applied with context:SetShader before drawing all my GUI?

Well yes that can be done by just rendering/drawing the scene/2D items to another buffer, apply the shader, and then draw the results to the default context buffer. Then to turn off/on you could set a toggle to not enable the shader in your code. Then you wouldn't have to edit shaders. It really depends on what you are comfortable with - shaders and buffer manipulation can be confusing for people just starting out.

 

function App:Start()
       self.window = Window:Create()
       self.context = Context:Create(self.window)
       self.buffer = Buffer:GetCurrent()

       self.myworld = World:Create()
       self.mybuffer = Buffer:Create(self.context:GetWidth(), self.context:GetHeight(),1,1)
       self.myshader = Shader:Load("Shaders/PostEffects/grayscale.shader")

       Map:Load("Maps/start.map")

       self.toggle = 0

       return true
end

function App:Loop()
       if self.window:Closed() or self.window:KeyHit(Key.Escape) then return false end

       Time:Update()
       self.myworld:Update()
       self.mybuffer:Enable()
       self.myworld:Render()
       self.context:SetBlendMode(Blend.Alpha)
       self.context:SetColor(1,0,0,1)
       self.context:DrawText("FPS: "..Time:UPS(), 0, 20)
       self.context:SetColor(1,1,1,1)
       self.context:SetBlendMode(Blend.Solid)

       self.buffer:Enable()
       if self.window:KeyHit(Key.Up) then self.toggle = 1 - self.toggle end
       if self.toggle==1 then
               self.myshader:Enable()
       else
               self.myshader:Disable()
       end
       self.mybuffer:GetColorTexture():Bind(1)
       self.context:DrawImage(self.mybuffer:GetColorTexture(),0,0,self.context:GetWidth(),self.context:GetHeight())
       self.myshader:Disable()
       self.context:Sync()

       return true
end

 

Keep in mind this code assumes that your scene has a camera in it either as an scene entity or created via an entity script like in the fpsplayer script.

Win7 64bit / Intel i7-2600 CPU @ 3.9 GHz / 16 GB DDR3 / NVIDIA GeForce GTX 590

LE / 3DWS / BMX / Hexagon

macklebee's channel

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...