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 ToneMap: ACES = object() PBR_NEUTRAL = object()
[docs]class FilterConfig: pass
[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