Source code for direct.filter.CommonFilters
"""
Class CommonFilters implements certain common image
postprocessing filters. See the :ref:`common-image-filters` page for
more information about how to use these filters.
These filters are written in the Cg shading language.
"""
# It is not ideal that these filters are all included in a single
# monolithic module. Unfortunately, when you want to apply two filters
# at the same time, you have to compose them into a single shader, and
# the composition process isn't simply a question of concatenating them:
# you have to somehow make them work together. I suspect that there
# exists some fairly simple framework that would make this automatable.
# However, until I write some more filters myself, I won't know what
# that framework is. Until then, I'll settle for this
# clunky approach. - Josh
from .FilterManager import FilterManager
from .filterBloomI import BLOOM_I
from .filterBloomX import BLOOM_X
from .filterBloomY import BLOOM_Y
from .filterBlurX import BLUR_X
from .filterBlurY import BLUR_Y
from .filterCopy import COPY
from .filterDown4 import DOWN_4
from panda3d.core import LVecBase4, LPoint2
from panda3d.core import Filename
from panda3d.core import AuxBitplaneAttrib, AntialiasAttrib
from panda3d.core import Texture, Shader, ATSNone
from panda3d.core import FrameBufferProperties
from panda3d.core import getDefaultCoordinateSystem, CS_zup_right, CS_zup_left
import os
CARTOON_BODY="""
float4 cartoondelta = k_cartoonseparation * texpix_txaux.xwyw;
float4 cartoon_c0 = tex2D(k_txaux, %(texcoord)s + cartoondelta.xy);
float4 cartoon_c1 = tex2D(k_txaux, %(texcoord)s - cartoondelta.xy);
float4 cartoon_c2 = tex2D(k_txaux, %(texcoord)s + cartoondelta.wz);
float4 cartoon_c3 = tex2D(k_txaux, %(texcoord)s - cartoondelta.wz);
float4 cartoon_mx = max(cartoon_c0, max(cartoon_c1, max(cartoon_c2, cartoon_c3)));
float4 cartoon_mn = min(cartoon_c0, min(cartoon_c1, min(cartoon_c2, cartoon_c3)));
float cartoon_thresh = saturate(dot(cartoon_mx - cartoon_mn, float4(3,3,0,0)) - 0.5);
o_color = lerp(o_color, k_cartooncolor, cartoon_thresh);
"""
# Some GPUs do not support variable-length loops.
#
# We fill in the actual value of numsamples in the loop limit
# when the shader is configured.
#
SSAO_BODY="""//Cg
void vshader(float4 vtx_position : POSITION,
float2 vtx_texcoord : TEXCOORD0,
out float4 l_position : POSITION,
out float2 l_texcoord : TEXCOORD0,
out float2 l_texcoordD : TEXCOORD1,
out float2 l_texcoordN : TEXCOORD2,
uniform float4 texpad_depth,
uniform float4 texpad_normal,
uniform float4x4 mat_modelproj)
{
l_position = mul(mat_modelproj, vtx_position);
l_texcoord = vtx_texcoord;
l_texcoordD = vtx_texcoord * texpad_depth.xy * 2;
l_texcoordN = vtx_texcoord * texpad_normal.xy * 2;
}
float3 sphere[16] = float3[](float3(0.53812504, 0.18565957, -0.43192),float3(0.13790712, 0.24864247, 0.44301823),float3(0.33715037, 0.56794053, -0.005789503),float3(-0.6999805, -0.04511441, -0.0019965635),float3(0.06896307, -0.15983082, -0.85477847),float3(0.056099437, 0.006954967, -0.1843352),float3(-0.014653638, 0.14027752, 0.0762037),float3(0.010019933, -0.1924225, -0.034443386),float3(-0.35775623, -0.5301969, -0.43581226),float3(-0.3169221, 0.106360726, 0.015860917),float3(0.010350345, -0.58698344, 0.0046293875),float3(-0.08972908, -0.49408212, 0.3287904),float3(0.7119986, -0.0154690035, -0.09183723),float3(-0.053382345, 0.059675813, -0.5411899),float3(0.035267662, -0.063188605, 0.54602677),float3(-0.47761092, 0.2847911, -0.0271716));
void fshader(out float4 o_color : COLOR,
uniform float4 k_params1,
uniform float4 k_params2,
float2 l_texcoord : TEXCOORD0,
float2 l_texcoordD : TEXCOORD1,
float2 l_texcoordN : TEXCOORD2,
uniform sampler2D k_random : TEXUNIT0,
uniform sampler2D k_depth : TEXUNIT1,
uniform sampler2D k_normal : TEXUNIT2)
{
float pixel_depth = tex2D(k_depth, l_texcoordD).a;
float3 pixel_normal = (tex2D(k_normal, l_texcoordN).xyz * 2.0 - 1.0);
float3 random_vector = normalize((tex2D(k_random, l_texcoord * 18.0 + pixel_depth + pixel_normal.xy).xyz * 2.0) - float3(1.0)).xyz;
float occlusion = 0.0;
float radius = k_params1.z / pixel_depth;
float depth_difference;
float3 sample_normal;
float3 ray;
for(int i = 0; i < %d; ++i) {
ray = radius * reflect(sphere[i], random_vector);
sample_normal = (tex2D(k_normal, l_texcoordN + ray.xy).xyz * 2.0 - 1.0);
depth_difference = (pixel_depth - tex2D(k_depth,l_texcoordD + ray.xy).r);
occlusion += step(k_params2.y, depth_difference) * (1.0 - dot(sample_normal.xyz, pixel_normal)) * (1.0 - smoothstep(k_params2.y, k_params2.x, depth_difference));
}
o_color.rgb = 1.0 + (occlusion * k_params1.y);
o_color.a = 1.0;
}
"""
[docs]class CommonFilters:
""" Class CommonFilters implements certain common image postprocessing
filters. The constructor requires a filter builder as a parameter. """
[docs] def __init__(self, win, cam):
self.manager = FilterManager(win, cam)
self.configuration = {}
self.task = None
self.cleanup()
[docs] def cleanup(self):
self.manager.cleanup()
self.textures = {}
self.finalQuad = None
self.bloom = []
self.blur = []
self.ssao = []
if self.task != None:
taskMgr.remove(self.task)
self.task = None
[docs] def reconfigure(self, fullrebuild, changed):
""" Reconfigure is called whenever any configuration change is made. """
configuration = self.configuration
if (fullrebuild):
self.cleanup()
if (len(configuration) == 0):
return
if not self.manager.win.gsg.getSupportsBasicShaders():
return False
auxbits = 0
needtex = set(["color"])
needtexcoord = set(["color"])
if ("CartoonInk" in configuration):
needtex.add("aux")
auxbits |= AuxBitplaneAttrib.ABOAuxNormal
needtexcoord.add("aux")
if ("AmbientOcclusion" in configuration):
needtex.add("depth")
needtex.add("ssao0")
needtex.add("ssao1")
needtex.add("ssao2")
needtex.add("aux")
auxbits |= AuxBitplaneAttrib.ABOAuxNormal
needtexcoord.add("ssao2")
if ("BlurSharpen" in configuration):
needtex.add("blur0")
needtex.add("blur1")
needtexcoord.add("blur1")
if ("Bloom" in configuration):
needtex.add("bloom0")
needtex.add("bloom1")
needtex.add("bloom2")
needtex.add("bloom3")
auxbits |= AuxBitplaneAttrib.ABOGlow
needtexcoord.add("bloom3")
if ("ViewGlow" in configuration):
auxbits |= AuxBitplaneAttrib.ABOGlow
if ("VolumetricLighting" in configuration):
needtex.add(configuration["VolumetricLighting"].source)
for tex in needtex:
self.textures[tex] = Texture("scene-" + tex)
self.textures[tex].setWrapU(Texture.WMClamp)
self.textures[tex].setWrapV(Texture.WMClamp)
fbprops = None
clamping = None
if "HighDynamicRange" in configuration:
fbprops = FrameBufferProperties()
fbprops.setFloatColor(True)
fbprops.setSrgbColor(False)
clamping = False
if "MSAA" in configuration:
if fbprops is None:
fbprops = FrameBufferProperties()
fbprops.setMultisamples(configuration["MSAA"].samples)
self.finalQuad = self.manager.renderSceneInto(textures = self.textures, auxbits=auxbits, fbprops=fbprops, clamping=clamping)
if (self.finalQuad == None):
self.cleanup()
return False
if "MSAA" in configuration:
camNode = self.manager.camera.node()
state = camNode.getInitialState()
state.setAttrib(AntialiasAttrib.make(AntialiasAttrib.M_multisample))
camNode.setInitialState(state)
if ("BlurSharpen" in configuration):
blur0=self.textures["blur0"]
blur1=self.textures["blur1"]
self.blur.append(self.manager.renderQuadInto("filter-blur0", colortex=blur0,div=2))
self.blur.append(self.manager.renderQuadInto("filter-blur1", colortex=blur1))
self.blur[0].setShaderInput("src", self.textures["color"])
self.blur[0].setShader(Shader.make(BLUR_X, Shader.SL_Cg))
self.blur[1].setShaderInput("src", blur0)
self.blur[1].setShader(Shader.make(BLUR_Y, Shader.SL_Cg))
if ("AmbientOcclusion" in configuration):
ssao0=self.textures["ssao0"]
ssao1=self.textures["ssao1"]
ssao2=self.textures["ssao2"]
self.ssao.append(self.manager.renderQuadInto("filter-ssao0", colortex=ssao0))
self.ssao.append(self.manager.renderQuadInto("filter-ssao1", colortex=ssao1,div=2))
self.ssao.append(self.manager.renderQuadInto("filter-ssao2", colortex=ssao2))
self.ssao[0].setShaderInput("depth", self.textures["depth"])
self.ssao[0].setShaderInput("normal", self.textures["aux"])
self.ssao[0].setShaderInput("random", loader.loadTexture("maps/random.rgb"))
self.ssao[0].setShader(Shader.make(SSAO_BODY % configuration["AmbientOcclusion"].numsamples, Shader.SL_Cg))
self.ssao[1].setShaderInput("src", ssao0)
self.ssao[1].setShader(Shader.make(BLUR_X, Shader.SL_Cg))
self.ssao[2].setShaderInput("src", ssao1)
self.ssao[2].setShader(Shader.make(BLUR_Y, Shader.SL_Cg))
if ("Bloom" in configuration):
bloomconf = configuration["Bloom"]
bloom0=self.textures["bloom0"]
bloom1=self.textures["bloom1"]
bloom2=self.textures["bloom2"]
bloom3=self.textures["bloom3"]
if (bloomconf.size == "large"):
scale=8
downsamplerName="filter-down4"
downsampler=DOWN_4
elif (bloomconf.size == "medium"):
scale=4
downsamplerName="filter-copy"
downsampler=COPY
else:
scale=2
downsamplerName="filter-copy"
downsampler=COPY
self.bloom.append(self.manager.renderQuadInto("filter-bloomi", colortex=bloom0, div=2, align=scale))
self.bloom.append(self.manager.renderQuadInto(downsamplerName, colortex=bloom1, div=scale, align=scale))
self.bloom.append(self.manager.renderQuadInto("filter-bloomx", colortex=bloom2, div=scale, align=scale))
self.bloom.append(self.manager.renderQuadInto("filter-bloomy", colortex=bloom3, div=scale, align=scale))
self.bloom[0].setShaderInput("src", self.textures["color"])
self.bloom[0].setShader(Shader.make(BLOOM_I, Shader.SL_Cg))
self.bloom[1].setShaderInput("src", bloom0)
self.bloom[1].setShader(Shader.make(downsampler, Shader.SL_Cg))
self.bloom[2].setShaderInput("src", bloom1)
self.bloom[2].setShader(Shader.make(BLOOM_X, Shader.SL_Cg))
self.bloom[3].setShaderInput("src", bloom2)
self.bloom[3].setShader(Shader.make(BLOOM_Y, Shader.SL_Cg))
texcoords = {}
texcoordPadding = {}
for tex in needtexcoord:
if self.textures[tex].getAutoTextureScale() != ATSNone or \
"HalfPixelShift" in configuration:
texcoords[tex] = "l_texcoord_" + tex
texcoordPadding["l_texcoord_" + tex] = tex
else:
# Share unpadded texture coordinates.
texcoords[tex] = "l_texcoord"
texcoordPadding["l_texcoord"] = None
texcoordSets = list(enumerate(texcoordPadding.keys()))
text = "//Cg\n"
if "HighDynamicRange" in configuration:
tonemap = configuration["HighDynamicRange"]
if tonemap is ToneMap.ACES:
text += "static const float3x3 aces_input_mat = {\n"
text += " {0.59719, 0.35458, 0.04823},\n"
text += " {0.07600, 0.90834, 0.01566},\n"
text += " {0.02840, 0.13383, 0.83777},\n"
text += "};\n"
text += "static const float3x3 aces_output_mat = {\n"
text += " { 1.60475, -0.53108, -0.07367},\n"
text += " {-0.10208, 1.10813, -0.00605},\n"
text += " {-0.00327, -0.07276, 1.07602},\n"
text += "};\n"
text += "void vshader(float4 vtx_position : POSITION,\n"
text += " out float4 l_position : POSITION,\n"
for texcoord, padTex in texcoordPadding.items():
if padTex is not None:
text += " uniform float4 texpad_tx%s,\n" % (padTex)
if ("HalfPixelShift" in configuration):
text += " uniform float4 texpix_tx%s,\n" % (padTex)
for i, name in texcoordSets:
text += " out float2 %s : TEXCOORD%d,\n" % (name, i)
text += " uniform float4x4 mat_modelproj)\n"
text += "{\n"
text += " l_position = mul(mat_modelproj, vtx_position);\n"
# The card is oriented differently depending on our chosen
# coordinate system. We could just use vtx_texcoord, but this
# saves on an additional variable.
if getDefaultCoordinateSystem() in (CS_zup_right, CS_zup_left):
pos = "vtx_position.xz"
else:
pos = "vtx_position.xy"
for texcoord, padTex in texcoordPadding.items():
if padTex is None:
text += " %s = %s * float2(0.5, 0.5) + float2(0.5, 0.5);\n" % (texcoord, pos)
else:
text += " %s = (%s * texpad_tx%s.xy) + texpad_tx%s.xy;\n" % (texcoord, pos, padTex, padTex)
if ("HalfPixelShift" in configuration):
text += " %s += texpix_tx%s.xy * 0.5;\n" % (texcoord, padTex)
text += "}\n"
text += "void fshader(\n"
for i, name in texcoordSets:
text += " float2 %s : TEXCOORD%d,\n" % (name, i)
for key in self.textures:
text += " uniform sampler2D k_tx" + key + ",\n"
if ("CartoonInk" in configuration):
text += " uniform float4 k_cartoonseparation,\n"
text += " uniform float4 k_cartooncolor,\n"
text += " uniform float4 texpix_txaux,\n"
if ("BlurSharpen" in configuration):
text += " uniform float4 k_blurval,\n"
if ("VolumetricLighting" in configuration):
text += " uniform float4 k_casterpos,\n"
text += " uniform float4 k_vlparams,\n"
if ("ExposureAdjust" in configuration):
text += " uniform float k_exposure,\n"
text += " out float4 o_color : COLOR)\n"
text += "{\n"
text += " o_color = tex2D(k_txcolor, %s);\n" % (texcoords["color"])
if ("CartoonInk" in configuration):
text += CARTOON_BODY % {"texcoord" : texcoords["aux"]}
if ("AmbientOcclusion" in configuration):
text += " o_color *= tex2D(k_txssao2, %s).r;\n" % (texcoords["ssao2"])
if ("BlurSharpen" in configuration):
text += " o_color = lerp(tex2D(k_txblur1, %s), o_color, k_blurval.x);\n" % (texcoords["blur1"])
if ("Bloom" in configuration):
text += " o_color = saturate(o_color);\n";
text += " float4 bloom = 0.5 * tex2D(k_txbloom3, %s);\n" % (texcoords["bloom3"])
text += " o_color = 1-((1-bloom)*(1-o_color));\n"
if ("ViewGlow" in configuration):
text += " o_color.r = o_color.a;\n"
if ("VolumetricLighting" in configuration):
text += " float decay = 1.0f;\n"
text += " float2 curcoord = %s;\n" % (texcoords["color"])
text += " float2 lightdir = curcoord - k_casterpos.xy;\n"
text += " lightdir *= k_vlparams.x;\n"
text += " half4 sample = tex2D(k_txcolor, curcoord);\n"
text += " float3 vlcolor = sample.rgb * sample.a;\n"
text += " for (int i = 0; i < %s; i++) {\n" % (int(configuration["VolumetricLighting"].numsamples))
text += " curcoord -= lightdir;\n"
text += " sample = tex2D(k_tx%s, curcoord);\n" % (configuration["VolumetricLighting"].source)
text += " sample *= sample.a * decay;//*weight\n"
text += " vlcolor += sample.rgb;\n"
text += " decay *= k_vlparams.y;\n"
text += " }\n"
text += " o_color += float4(vlcolor * k_vlparams.z, 1);\n"
if ("ExposureAdjust" in configuration):
text += " o_color.rgb *= k_exposure;\n"
if "HighDynamicRange" in configuration:
tonemap = configuration["HighDynamicRange"]
if tonemap is ToneMap.ACES:
# With thanks to Stephen Hill!
text += " float3 aces_color = mul(aces_input_mat, o_color.rgb);\n"
text += " o_color.rgb = saturate(mul(aces_output_mat, (aces_color * (aces_color + 0.0245786f) - 0.000090537f) / (aces_color * (0.983729f * aces_color + 0.4329510f) + 0.238081f)));\n"
elif tonemap is ToneMap.PBR_NEUTRAL:
text += " const float start_compression = 0.8 - 0.04;\n"
text += " const float desaturation = 0.15;\n"
text += " float x = min(o_color.r, min(o_color.g, o_color.b));\n"
text += " float offset = x < 0.08 ? x - 6.25 * x * x : 0.04;\n"
text += " o_color.rgb -= offset;\n"
text += " float peak = max(o_color.r, max(o_color.g, o_color.b));\n"
text += " if (peak >= start_compression) {\n"
text += " const float d = 1.0 - start_compression;\n"
text += " float new_peak = 1.0 - d * d / (peak + d - start_compression);\n"
text += " o_color.rgb *= new_peak / peak;\n"
text += " float g = 1.0 - 1.0 / (desaturation * (peak - new_peak) + 1.0);\n"
text += " o_color.rgb = lerp(o_color.rgb, new_peak * float3(1, 1, 1), g);\n"
text += "}\n"
if ("GammaAdjust" in configuration):
gamma = configuration["GammaAdjust"]
if gamma == 0.5:
text += " o_color.rgb = sqrt(o_color.rgb);\n"
elif gamma == 2.0:
text += " o_color.rgb *= o_color.rgb;\n"
elif gamma != 1.0:
text += " o_color.rgb = pow(o_color.rgb, %ff);\n" % (gamma)
if ("SrgbEncode" in configuration):
text += " o_color.r = (o_color.r < 0.0031308) ? (o_color.r * 12.92) : (1.055 * pow(o_color.r, 0.41666) - 0.055);\n"
text += " o_color.g = (o_color.g < 0.0031308) ? (o_color.g * 12.92) : (1.055 * pow(o_color.g, 0.41666) - 0.055);\n"
text += " o_color.b = (o_color.b < 0.0031308) ? (o_color.b * 12.92) : (1.055 * pow(o_color.b, 0.41666) - 0.055);\n"
if ("Inverted" in configuration):
text += " o_color = float4(1, 1, 1, 1) - o_color;\n"
text += "}\n"
shader = Shader.make(text, Shader.SL_Cg)
if not shader:
return False
self.finalQuad.setShader(shader)
for tex in self.textures:
self.finalQuad.setShaderInput("tx"+tex, self.textures[tex])
self.task = taskMgr.add(self.update, "common-filters-update")
if (changed == "CartoonInk") or fullrebuild:
if ("CartoonInk" in configuration):
c = configuration["CartoonInk"]
self.finalQuad.setShaderInput("cartoonseparation", LVecBase4(c.separation, 0, c.separation, 0))
self.finalQuad.setShaderInput("cartooncolor", c.color)
if (changed == "BlurSharpen") or fullrebuild:
if ("BlurSharpen" in configuration):
blurval = configuration["BlurSharpen"]
self.finalQuad.setShaderInput("blurval", LVecBase4(blurval, blurval, blurval, blurval))
if (changed == "Bloom") or fullrebuild:
if ("Bloom" in configuration):
bloomconf = configuration["Bloom"]
intensity = bloomconf.intensity * 3.0
self.bloom[0].setShaderInput("blend", bloomconf.blendx, bloomconf.blendy, bloomconf.blendz, bloomconf.blendw * 2.0)
self.bloom[0].setShaderInput("trigger", bloomconf.mintrigger, 1.0/(bloomconf.maxtrigger-bloomconf.mintrigger), 0.0, 0.0)
self.bloom[0].setShaderInput("desat", bloomconf.desat)
self.bloom[3].setShaderInput("intensity", intensity, intensity, intensity, intensity)
if (changed == "VolumetricLighting") or fullrebuild:
if ("VolumetricLighting" in configuration):
config = configuration["VolumetricLighting"]
tcparam = config.density / float(config.numsamples)
self.finalQuad.setShaderInput("vlparams", tcparam, config.decay, config.exposure, 0.0)
if (changed == "AmbientOcclusion") or fullrebuild:
if ("AmbientOcclusion" in configuration):
config = configuration["AmbientOcclusion"]
self.ssao[0].setShaderInput("params1", config.numsamples, -float(config.amount) / config.numsamples, config.radius, 0)
self.ssao[0].setShaderInput("params2", config.strength, config.falloff, 0, 0)
if (changed == "ExposureAdjust") or fullrebuild:
if ("ExposureAdjust" in configuration):
stops = configuration["ExposureAdjust"]
self.finalQuad.setShaderInput("exposure", 2 ** stops)
self.update()
return True
[docs] def update(self, task = None):
"""Updates the shader inputs that need to be updated every frame.
Normally, you shouldn't call this, it's being called in a task."""
if "VolumetricLighting" in self.configuration:
caster = self.configuration["VolumetricLighting"].caster
casterpos = LPoint2()
self.manager.camera.node().getLens().project(caster.getPos(self.manager.camera), casterpos)
self.finalQuad.setShaderInput("casterpos", LVecBase4(casterpos.getX() * 0.5 + 0.5, (casterpos.getY() * 0.5 + 0.5), 0, 0))
if task != None:
return task.cont
[docs] def setMSAA(self, samples):
"""Enables multisample anti-aliasing on the render-to-texture buffer.
If you enable this, it is recommended to leave any multisample request
on the main framebuffer OFF (ie. don't set framebuffer-multisample true
in Config.prc), since it would be a waste of resources otherwise.
.. versionadded:: 1.10.13
"""
fullrebuild = "MSAA" not in self.configuration or self.configuration["MSAA"].samples != samples
newconfig = FilterConfig()
newconfig.samples = samples
self.configuration["MSAA"] = newconfig
return self.reconfigure(fullrebuild, "MSAA")
[docs] def delMSAA(self):
if "MSAA" in self.configuration:
del self.configuration["MSAA"]
return self.reconfigure(True, "MSAA")
return True
[docs] def setCartoonInk(self, separation=1, color=(0, 0, 0, 1)):
fullrebuild = (("CartoonInk" in self.configuration) == False)
newconfig = FilterConfig()
newconfig.separation = separation
newconfig.color = color
self.configuration["CartoonInk"] = newconfig
return self.reconfigure(fullrebuild, "CartoonInk")
[docs] def delCartoonInk(self):
if ("CartoonInk" in self.configuration):
del self.configuration["CartoonInk"]
return self.reconfigure(True, "CartoonInk")
return True
[docs] def setBloom(self, blend=(0.3,0.4,0.3,0.0), mintrigger=0.6, maxtrigger=1.0, desat=0.6, intensity=1.0, size="medium"):
"""
Applies the Bloom filter to the output.
size can either be "off", "small", "medium", or "large".
Setting size to "off" will remove the Bloom filter.
"""
if (size==0): size="off"
elif (size==1): size="small"
elif (size==2): size="medium"
elif (size==3): size="large"
if (size=="off"):
self.delBloom()
return
if (maxtrigger==None): maxtrigger=mintrigger+0.8
oldconfig = self.configuration.get("Bloom", None)
fullrebuild = True
if (oldconfig) and (oldconfig.size == size):
fullrebuild = False
newconfig = FilterConfig()
(newconfig.blendx, newconfig.blendy, newconfig.blendz, newconfig.blendw) = blend
newconfig.maxtrigger = maxtrigger
newconfig.mintrigger = mintrigger
newconfig.desat = desat
newconfig.intensity = intensity
newconfig.size = size
self.configuration["Bloom"] = newconfig
return self.reconfigure(fullrebuild, "Bloom")
[docs] def delBloom(self):
if ("Bloom" in self.configuration):
del self.configuration["Bloom"]
return self.reconfigure(True, "Bloom")
return True
[docs] def setHalfPixelShift(self):
fullrebuild = (("HalfPixelShift" in self.configuration) == False)
self.configuration["HalfPixelShift"] = 1
return self.reconfigure(fullrebuild, "HalfPixelShift")
[docs] def delHalfPixelShift(self):
if ("HalfPixelShift" in self.configuration):
del self.configuration["HalfPixelShift"]
return self.reconfigure(True, "HalfPixelShift")
return True
[docs] def setViewGlow(self):
fullrebuild = (("ViewGlow" in self.configuration) == False)
self.configuration["ViewGlow"] = 1
return self.reconfigure(fullrebuild, "ViewGlow")
[docs] def delViewGlow(self):
if ("ViewGlow" in self.configuration):
del self.configuration["ViewGlow"]
return self.reconfigure(True, "ViewGlow")
return True
[docs] def setInverted(self):
fullrebuild = (("Inverted" in self.configuration) == False)
self.configuration["Inverted"] = 1
return self.reconfigure(fullrebuild, "Inverted")
[docs] def delInverted(self):
if ("Inverted" in self.configuration):
del self.configuration["Inverted"]
return self.reconfigure(True, "Inverted")
return True
[docs] def setVolumetricLighting(self, caster, numsamples = 32, density = 5.0, decay = 0.1, exposure = 0.1, source = "color"):
oldconfig = self.configuration.get("VolumetricLighting", None)
fullrebuild = True
if (oldconfig) and (oldconfig.source == source) and (oldconfig.numsamples == int(numsamples)):
fullrebuild = False
newconfig = FilterConfig()
newconfig.caster = caster
newconfig.numsamples = int(numsamples)
newconfig.density = density
newconfig.decay = decay
newconfig.exposure = exposure
newconfig.source = source
self.configuration["VolumetricLighting"] = newconfig
return self.reconfigure(fullrebuild, "VolumetricLighting")
[docs] def delVolumetricLighting(self):
if ("VolumetricLighting" in self.configuration):
del self.configuration["VolumetricLighting"]
return self.reconfigure(True, "VolumetricLighting")
return True
[docs] def setBlurSharpen(self, amount=0.0):
"""Enables the blur/sharpen filter. If the 'amount' parameter is 1.0, it will not have any effect.
A value of 0.0 means fully blurred, and a value higher than 1.0 sharpens the image."""
fullrebuild = (("BlurSharpen" in self.configuration) == False)
self.configuration["BlurSharpen"] = amount
return self.reconfigure(fullrebuild, "BlurSharpen")
[docs] def delBlurSharpen(self):
if ("BlurSharpen" in self.configuration):
del self.configuration["BlurSharpen"]
return self.reconfigure(True, "BlurSharpen")
return True
[docs] def setAmbientOcclusion(self, numsamples = 16, radius = 0.05, amount = 2.0, strength = 0.01, falloff = 0.000002):
fullrebuild = (("AmbientOcclusion" in self.configuration) == False)
if (not fullrebuild):
fullrebuild = (numsamples != self.configuration["AmbientOcclusion"].numsamples)
newconfig = FilterConfig()
newconfig.numsamples = numsamples
newconfig.radius = radius
newconfig.amount = amount
newconfig.strength = strength
newconfig.falloff = falloff
self.configuration["AmbientOcclusion"] = newconfig
return self.reconfigure(fullrebuild, "AmbientOcclusion")
[docs] def delAmbientOcclusion(self):
if ("AmbientOcclusion" in self.configuration):
del self.configuration["AmbientOcclusion"]
return self.reconfigure(True, "AmbientOcclusion")
return True
[docs] def setGammaAdjust(self, gamma):
""" Applies additional gamma correction to the image. 1.0 = no correction. """
old_gamma = self.configuration.get("GammaAdjust", 1.0)
if old_gamma != gamma:
self.configuration["GammaAdjust"] = gamma
return self.reconfigure(True, "GammaAdjust")
return True
[docs] def delGammaAdjust(self):
if ("GammaAdjust" in self.configuration):
old_gamma = self.configuration["GammaAdjust"]
del self.configuration["GammaAdjust"]
return self.reconfigure((old_gamma != 1.0), "GammaAdjust")
return True
[docs] def setSrgbEncode(self, force=False):
""" Applies the inverse sRGB EOTF to the output, unless the window
already has an sRGB framebuffer, in which case this filter refuses to
apply, to prevent accidental double-application.
Set the force argument to True to force it to be applied in all cases.
.. versionadded:: 1.10.7
"""
new_enable = force or not self.manager.win.getFbProperties().getSrgbColor()
old_enable = self.configuration.get("SrgbEncode", False)
if new_enable and not old_enable:
self.configuration["SrgbEncode"] = True
return self.reconfigure(True, "SrgbEncode")
elif not new_enable and old_enable:
del self.configuration["SrgbEncode"]
return new_enable
[docs] def delSrgbEncode(self):
""" Reverses the effects of setSrgbEncode. """
if ("SrgbEncode" in self.configuration):
old_enable = self.configuration["SrgbEncode"]
del self.configuration["SrgbEncode"]
return self.reconfigure(old_enable, "SrgbEncode")
return True
[docs] def setHighDynamicRange(self, tonemap=ToneMap.ACES):
""" Enables HDR rendering by using a floating-point framebuffer,
disabling color clamping on the main scene, and applying a tone map
operator (ACES or Khronos PBR Neutral).
It may also be necessary to use setExposureAdjust to perform exposure
compensation on the scene, depending on the lighting intensity.
.. versionadded:: 1.10.7
"""
fullrebuild = "HighDynamicRange" not in self.configuration or \
self.configuration["HighDynamicRange"] is not tonemap
if tonemap is not ToneMap.ACES and tonemap is not ToneMap.PBR_NEUTRAL:
raise ValueError("Invalid value for tonemap")
self.configuration["HighDynamicRange"] = tonemap
return self.reconfigure(fullrebuild, "HighDynamicRange")
[docs] def delHighDynamicRange(self):
if ("HighDynamicRange" in self.configuration):
del self.configuration["HighDynamicRange"]
return self.reconfigure(True, "HighDynamicRange")
return True
[docs] def setExposureAdjust(self, stops):
""" Sets a relative exposure adjustment to multiply with the result of
rendering the scene, in stops. A value of 0 means no adjustment, a
positive value will result in a brighter image. Useful in conjunction
with HDR, see setHighDynamicRange.
.. versionadded:: 1.10.7
"""
old_stops = self.configuration.get("ExposureAdjust")
if old_stops != stops:
self.configuration["ExposureAdjust"] = stops
return self.reconfigure(old_stops is None, "ExposureAdjust")
return True
[docs] def delExposureAdjust(self):
if ("ExposureAdjust" in self.configuration):
del self.configuration["ExposureAdjust"]
return self.reconfigure(True, "ExposureAdjust")
return True
#snake_case alias:
set_msaa = setMSAA
del_msaa = delMSAA
del_cartoon_ink = delCartoonInk
set_half_pixel_shift = setHalfPixelShift
del_half_pixel_shift = delHalfPixelShift
set_inverted = setInverted
del_inverted = delInverted
del_view_glow = delViewGlow
set_volumetric_lighting = setVolumetricLighting
set_bloom = setBloom
set_view_glow = setViewGlow
set_ambient_occlusion = setAmbientOcclusion
set_cartoon_ink = setCartoonInk
del_bloom = delBloom
del_ambient_occlusion = delAmbientOcclusion
set_blur_sharpen = setBlurSharpen
del_blur_sharpen = delBlurSharpen
del_volumetric_lighting = delVolumetricLighting
set_gamma_adjust = setGammaAdjust
del_gamma_adjust = delGammaAdjust
set_srgb_encode = setSrgbEncode
del_srgb_encode = delSrgbEncode
set_exposure_adjust = setExposureAdjust
del_exposure_adjust = delExposureAdjust
set_high_dynamic_range = setHighDynamicRange
del_high_dynamic_range = delHighDynamicRange