def __init__(self, N, sourceTex, normalizationFactor): """ Creates a new fft instance. The source texture has to specified from the begining, as the shaderAttributes are pregenerated for performance reasons """ DebugObject.__init__(self, "GPU-FFT") self.size = N self.log2Size = int(math.log(N, 2)) self.normalizationFactor = normalizationFactor # Create a ping and a pong texture, because we can't write to the # same texture while reading to it (that would lead to unexpected # behaviour, we could solve that by using an appropriate thread size, # but it works fine so far) self.pingTexture = Texture("FFTPing") self.pingTexture.setup2dTexture(self.size, self.size, Texture.TFloat, Texture.FRgba32) self.pongTexture = Texture("FFTPong") self.pongTexture.setup2dTexture(self.size, self.size, Texture.TFloat, Texture.FRgba32) self.sourceTex = sourceTex for tex in [self.pingTexture, self.pongTexture, sourceTex]: tex.setMinfilter(Texture.FTNearest) tex.setMagfilter(Texture.FTNearest) tex.setWrapU(Texture.WMClamp) tex.setWrapV(Texture.WMClamp) # Pregenerate weights & indices for the shaders self._computeWeighting() # Pre generate the shaders, we have 2 passes: Horizontal and Vertical # which both execute log2(N) times with varying radii self.horizontalFFTShader = Shader.loadCompute( Shader.SLGLSL, "Shader/WaterFFT/HorizontalFFT.compute") self.horizontalFFT = NodePath("HorizontalFFT") self.horizontalFFT.setShader(self.horizontalFFTShader) self.horizontalFFT.setShaderInput("precomputedWeights", self.weightsLookupTex) self.horizontalFFT.setShaderInput("N", LVecBase2i(self.size)) self.verticalFFTShader = Shader.loadCompute( Shader.SLGLSL, "Shader/WaterFFT/VerticalFFT.compute") self.verticalFFT = NodePath("VerticalFFT") self.verticalFFT.setShader(self.verticalFFTShader) self.verticalFFT.setShaderInput("precomputedWeights", self.weightsLookupTex) self.verticalFFT.setShaderInput("N", LVecBase2i(self.size)) # Create a texture where the result is stored self.resultTexture = Texture("Result") self.resultTexture.setup2dTexture(self.size, self.size, Texture.TFloat, Texture.FRgba16) self.resultTexture.setMinfilter(Texture.FTLinear) self.resultTexture.setMagfilter(Texture.FTLinear) # Prepare the shader attributes, so we don't have to regenerate them # every frame -> That is VERY slow (3ms per fft instance) self._prepareAttributes()
def __init__(self, N, sourceTex, normalizationFactor): """ Creates a new fft instance. The source texture has to specified from the begining, as the shaderAttributes are pregenerated for performance reasons """ DebugObject.__init__(self, "GPU-FFT") self.size = N self.log2Size = int(math.log(N, 2)) self.normalizationFactor = normalizationFactor # Create a ping and a pong texture, because we can't write to the # same texture while reading to it (that would lead to unexpected # behaviour, we could solve that by using an appropriate thread size, # but it works fine so far) self.pingTexture = Texture("FFTPing") self.pingTexture.setup2dTexture( self.size, self.size, Texture.TFloat, Texture.FRgba32) self.pongTexture = Texture("FFTPong") self.pongTexture.setup2dTexture( self.size, self.size, Texture.TFloat, Texture.FRgba32) self.sourceTex = sourceTex for tex in [self.pingTexture, self.pongTexture, sourceTex]: tex.setMinfilter(Texture.FTNearest) tex.setMagfilter(Texture.FTNearest) tex.setWrapU(Texture.WMClamp) tex.setWrapV(Texture.WMClamp) # Pregenerate weights & indices for the shaders self._computeWeighting() # Pre generate the shaders, we have 2 passes: Horizontal and Vertical # which both execute log2(N) times with varying radii self.horizontalFFTShader = Shader.loadCompute(Shader.SLGLSL, "Shader/Water/HorizontalFFT.compute") self.horizontalFFT = NodePath("HorizontalFFT") self.horizontalFFT.setShader(self.horizontalFFTShader) self.horizontalFFT.setShaderInput( "precomputedWeights", self.weightsLookupTex) self.horizontalFFT.setShaderInput("N", LVecBase2i(self.size)) self.verticalFFTShader = Shader.loadCompute(Shader.SLGLSL, "Shader/Water/VerticalFFT.compute") self.verticalFFT = NodePath("VerticalFFT") self.verticalFFT.setShader(self.verticalFFTShader) self.verticalFFT.setShaderInput( "precomputedWeights", self.weightsLookupTex) self.verticalFFT.setShaderInput("N", LVecBase2i(self.size)) # Create a texture where the result is stored self.resultTexture = Texture("Result") self.resultTexture.setup2dTexture( self.size, self.size, Texture.TFloat, Texture.FRgba16) self.resultTexture.setMinfilter(Texture.FTLinear) self.resultTexture.setMagfilter(Texture.FTLinear) # Prepare the shader attributes, so we don't have to regenerate them # every frame -> That is VERY slow (3ms per fft instance) self._prepareAttributes()
def _createInitialGrid(self): """ Creates the initial cloud grid """ shader = Shader.loadCompute(Shader.SLGLSL, "Shader/Clouds/InitialGrid.compute") dummy = NodePath("dummy") dummy.setShader(shader) dummy.setShaderInput("cloudGrid", self.voxelGrid) sattr = dummy.getAttrib(ShaderAttrib) Globals.base.graphicsEngine.dispatch_compute( (self.cloudResolution / 8, self.cloudResolution / 8, self.cloudResolutionH / 8), sattr, Globals.base.win.getGsg()) shader = Shader.loadCompute(Shader.SLGLSL, "Shader/Clouds/CloudNoise.compute") dummy = NodePath("dummy") dummy.setShader(shader) dummy.setShaderInput("noiseGrid", self.cloudNoise) sattr = dummy.getAttrib(ShaderAttrib) Globals.base.graphicsEngine.dispatch_compute( (64 / 8, 64 / 8, 64 / 8), sattr, Globals.base.win.getGsg())
def _createInitialGrid(self): """ Creates the initial cloud grid """ shader = Shader.loadCompute(Shader.SLGLSL, "Shader/Clouds/InitialGrid.compute") dummy = NodePath("dummy") dummy.setShader(shader) dummy.setShaderInput("cloudGrid", self.voxelGrid) sattr = dummy.getAttrib(ShaderAttrib) Globals.base.graphicsEngine.dispatch_compute( (self.cloudResolution / 8, self.cloudResolution / 8, self.cloudResolutionH / 8), sattr, Globals.base.win.getGsg()) shader = Shader.loadCompute(Shader.SLGLSL, "Shader/Clouds/CloudNoise.compute") dummy = NodePath("dummy") dummy.setShader(shader) dummy.setShaderInput("noiseGrid", self.cloudNoise) sattr = dummy.getAttrib(ShaderAttrib) Globals.base.graphicsEngine.dispatch_compute((64 / 8, 64 / 8, 64 / 8), sattr, Globals.base.win.getGsg())
def enable(self): if not self.isSupported(): # HDR/auto exposure is implemented using compute # shaders, which are only supported by more modern # graphics cards and requires at least OpenGL 4.3. return self.sceneTex = Texture('hdrSceneTex') self.sceneTex.setup2dTexture(self.Size, self.Size, Texture.TFloat, Texture.FRgba32) self.sceneTex.setWrapU(SamplerState.WMClamp) self.sceneTex.setWrapV(SamplerState.WMClamp) self.sceneTex.clearImage() self.__setupSceneQuad() # Build luminance histogram bucket ranges. self.ptaBucketRange = PTALVecBase2f.emptyArray(self.NumBuckets) for i in xrange(self.NumBuckets): # Use even distribution bmin = float(i) / float(self.NumBuckets) bmax = float(i + 1) / float(self.NumBuckets) # Use a distribution with slightly more bins in the low range. if bmin > 0.0: bmin = math.pow(bmin, 1.5) if bmax > 0.0: bmax = math.pow(bmax, 1.5) self.ptaBucketRange.setElement(i, Vec2(bmin, bmax)) self.histogramTex = Texture('histogram') self.histogramTex.setup1dTexture(self.NumBuckets, Texture.TInt, Texture.FR32i) self.histogramTex.setClearColor(Vec4(0)) self.histogramTex.clearImage() self.histogramCompute = base.computeRoot.attachNewNode(ComputeNode('histogramCompute')) self.histogramCompute.node().addDispatch(self.Size / 8, self.Size / 8, self.Size / 16) self.histogramCompute.setShader(Shader.loadCompute(Shader.SLGLSL, "phase_14/models/shaders/build_histogram.compute.glsl"), 1) self.histogramCompute.setShaderInput("scene_texture", self.sceneTex) self.histogramCompute.setShaderInput("histogram_texture", self.histogramTex) self.histogramCompute.setShaderInput("bucketrange", self.ptaBucketRange) self.histogramCompute.setBin("fixed", 0) self.exposureTex = Texture('exposure') self.exposureTex.setup1dTexture(1, Texture.TFloat, Texture.FR16) self.exposureTex.setClearColor(Vec4(0.0)) self.exposureTex.clearImage() self.exposureCompute = base.computeRoot.attachNewNode(ComputeNode('exposureCompute')) self.exposureCompute.node().addDispatch(1, 1, 1) self.exposureCompute.setShader(Shader.loadCompute(Shader.SLGLSL, "phase_14/models/shaders/calc_luminance.compute.glsl"), 1) self.exposureCompute.setShaderInput("histogram_texture", self.histogramTex) self.exposureCompute.setShaderInput("avg_lum_texture", self.exposureTex) self.exposureCompute.setShaderInput("bucketrange", self.ptaBucketRange) self.exposureCompute.setShaderInput("exposure_minmax", Vec2(1.0, 2.0)) self.exposureCompute.setShaderInput("adaption_rate_brightdark", Vec2(0.6, 0.6)) self.exposureCompute.setShaderInput("exposure_scale", base.config.GetFloat("hdr-tonemapscale", 1.75)) self.exposureCompute.setShaderInput("config_minAvgLum", base.config.GetFloat("hdr-min-avglum", 3.0)) self.exposureCompute.setShaderInput("config_perctBrightPixels", base.config.GetFloat("hdr-percent-bright-pixels", 2.0)) self.exposureCompute.setShaderInput("config_perctTarget", base.config.GetFloat("hdr-percent-target", 60.0)) self.exposureCompute.setBin("fixed", 1) base.filters.setExposure(self.exposureTex) taskMgr.add(self.__update, "hdrUpdate", sort = -10000000) if base.config.GetBool("hdr-debug-histogram", False): self.debugTex = Texture('histogramDebugTex') self.debugTex.setup2dTexture(self.NumBuckets, 1, Texture.TFloat, Texture.FRgba32) self.debugTex.setMagfilter(SamplerState.FTNearest) self.debugTex.setClearColor(Vec4(0.3, 0.3, 0.5, 1.0)) self.debugTex.clearImage() self.debugCompute = base.computeRoot.attachNewNode(ComputeNode('debugHistogramCompute')) self.debugCompute.node().addDispatch(1, 1, 1) self.debugCompute.setShader(Shader.loadCompute(Shader.SLGLSL, "phase_14/models/shaders/debug_histogram.compute.glsl"), 1) self.debugCompute.setShaderInput("histogram_texture", self.histogramTex) self.debugCompute.setShaderInput("debug_texture", self.debugTex) self.debugCompute.setBin("fixed", 2) self.debugImg = OnscreenImage(image = self.debugTex, scale = 0.3, pos = (-0.6, -0.7, -0.7))
def __init__(self): DebugObject.__init__(self, "WaterManager") self.options = OceanOptions() self.options.size = 512 self.options.windDir.normalize() self.options.waveAmplitude *= 1e-7 self.displacementTex = Texture("Displacement") self.displacementTex.setup2dTexture( self.options.size, self.options.size, Texture.TFloat, Texture.FRgba16) self.normalTex = Texture("Normal") self.normalTex.setup2dTexture( self.options.size, self.options.size, Texture.TFloat, Texture.FRgba16) self.combineShader = Shader.loadCompute(Shader.SLGLSL, "Shader/WaterFFT/Combine.compute") self.ptaTime = PTAFloat.emptyArray(1) # Create a gaussian random texture, as shaders aren't well suited # for that setRandomSeed(523) self.randomStorage = PNMImage(self.options.size, self.options.size, 4) self.randomStorage.setMaxval((2 ** 16) - 1) for x in xrange(self.options.size): for y in xrange(self.options.size): rand1 = self._getGaussianRandom() / 10.0 + 0.5 rand2 = self._getGaussianRandom() / 10.0 + 0.5 self.randomStorage.setXel(x, y, float(rand1), float(rand2), 0) self.randomStorage.setAlpha(x, y, 1.0) self.randomStorageTex = Texture("RandomStorage") self.randomStorageTex.load(self.randomStorage) self.randomStorageTex.setFormat(Texture.FRgba16) self.randomStorageTex.setMinfilter(Texture.FTNearest) self.randomStorageTex.setMagfilter(Texture.FTNearest) # Create the texture wwhere the intial height (H0 + Omega0) is stored. self.texInitialHeight = Texture("InitialHeight") self.texInitialHeight.setup2dTexture( self.options.size, self.options.size, Texture.TFloat, Texture.FRgba16) self.texInitialHeight.setMinfilter(Texture.FTNearest) self.texInitialHeight.setMagfilter(Texture.FTNearest) # Create the shader which populates the initial height texture self.shaderInitialHeight = Shader.loadCompute(Shader.SLGLSL, "Shader/WaterFFT/InitialHeight.compute") self.nodeInitialHeight = NodePath("initialHeight") self.nodeInitialHeight.setShader(self.shaderInitialHeight) self.nodeInitialHeight.setShaderInput("dest", self.texInitialHeight) self.nodeInitialHeight.setShaderInput( "N", LVecBase2i(self.options.size)) self.nodeInitialHeight.setShaderInput( "patchLength", self.options.patchLength) self.nodeInitialHeight.setShaderInput("windDir", self.options.windDir) self.nodeInitialHeight.setShaderInput( "windSpeed", self.options.windSpeed) self.nodeInitialHeight.setShaderInput( "waveAmplitude", self.options.waveAmplitude) self.nodeInitialHeight.setShaderInput( "windDependency", self.options.windDependency) self.nodeInitialHeight.setShaderInput( "randomTex", self.randomStorageTex) self.attrInitialHeight = self.nodeInitialHeight.getAttrib(ShaderAttrib) self.heightTextures = [] for i in xrange(3): tex = Texture("Height") tex.setup2dTexture(self.options.size, self.options.size, Texture.TFloat, Texture.FRgba16) tex.setMinfilter(Texture.FTNearest) tex.setMagfilter(Texture.FTNearest) tex.setWrapU(Texture.WMClamp) tex.setWrapV(Texture.WMClamp) self.heightTextures.append(tex) # Also create the shader which updates the spectrum self.shaderUpdate = Shader.loadCompute(Shader.SLGLSL, "Shader/WaterFFT/Update.compute") self.nodeUpdate = NodePath("update") self.nodeUpdate.setShader(self.shaderUpdate) self.nodeUpdate.setShaderInput("outH0x", self.heightTextures[0]) self.nodeUpdate.setShaderInput("outH0y", self.heightTextures[1]) self.nodeUpdate.setShaderInput("outH0z", self.heightTextures[2]) self.nodeUpdate.setShaderInput("initialHeight", self.texInitialHeight) self.nodeUpdate.setShaderInput("N", LVecBase2i(self.options.size)) self.nodeUpdate.setShaderInput("time", self.ptaTime) self.attrUpdate = self.nodeUpdate.getAttrib(ShaderAttrib) # Create 3 FFTs self.fftX = GPUFFT(self.options.size, self.heightTextures[0], self.options.normalizationFactor) self.fftY = GPUFFT(self.options.size, self.heightTextures[1], self.options.normalizationFactor) self.fftZ = GPUFFT(self.options.size, self.heightTextures[2], self.options.normalizationFactor) self.combineNode = NodePath("Combine") self.combineNode.setShader(self.combineShader) self.combineNode.setShaderInput( "displacementX", self.fftX.getResultTexture()) self.combineNode.setShaderInput( "displacementY", self.fftY.getResultTexture()) self.combineNode.setShaderInput( "displacementZ", self.fftZ.getResultTexture()) self.combineNode.setShaderInput("normalDest", self.normalTex) self.combineNode.setShaderInput( "displacementDest", self.displacementTex) self.combineNode.setShaderInput( "N", LVecBase2i(self.options.size)) self.combineNode.setShaderInput( "choppyScale", self.options.choppyScale) self.combineNode.setShaderInput( "gridLength", self.options.patchLength) # Store only the shader attrib as this is way faster self.attrCombine = self.combineNode.getAttrib(ShaderAttrib)
def __init__(self): DebugObject.__init__(self, "WaterManager") self.options = OceanOptions() self.options.size = 512 self.options.windDir.normalize() self.options.waveAmplitude *= 1e-7 self.displacementTex = Texture("Displacement") self.displacementTex.setup2dTexture( self.options.size, self.options.size, Texture.TFloat, Texture.FRgba16) self.normalTex = Texture("Normal") self.normalTex.setup2dTexture( self.options.size, self.options.size, Texture.TFloat, Texture.FRgba16) self.combineShader = Shader.loadCompute(Shader.SLGLSL, "Shader/WaterFFT/Combine.compute") self.ptaTime = PTAFloat.emptyArray(1) # Create a gaussian random texture, as shaders aren't well suited # for that setRandomSeed(523) self.randomStorage = PNMImage(self.options.size, self.options.size, 4) self.randomStorage.setMaxval((2 ** 16) - 1) for x in xrange(self.options.size): for y in xrange(self.options.size): rand1 = self._getGaussianRandom() / 10.0 + 0.5 rand2 = self._getGaussianRandom() / 10.0 + 0.5 self.randomStorage.setXel(x, y, float(rand1), float(rand2), 0) self.randomStorage.setAlpha(x, y, 1.0) self.randomStorageTex = Texture("RandomStorage") self.randomStorageTex.load(self.randomStorage) self.randomStorageTex.setFormat(Texture.FRgba16) self.randomStorageTex.setMinfilter(Texture.FTNearest) self.randomStorageTex.setMagfilter(Texture.FTNearest) # Create the texture wwhere the intial height (H0 + Omega0) is stored. self.texInitialHeight = Texture("InitialHeight") self.texInitialHeight.setup2dTexture( self.options.size, self.options.size, Texture.TFloat, Texture.FRgba16) self.texInitialHeight.setMinfilter(Texture.FTNearest) self.texInitialHeight.setMagfilter(Texture.FTNearest) # Create the shader which populates the initial height texture self.shaderInitialHeight = Shader.loadCompute(Shader.SLGLSL, "Shader/WaterFFT/InitialHeight.compute") self.nodeInitialHeight = NodePath("initialHeight") self.nodeInitialHeight.setShader(self.shaderInitialHeight) self.nodeInitialHeight.setShaderInput("dest", self.texInitialHeight) self.nodeInitialHeight.setShaderInput( "N", LVecBase2i(self.options.size)) self.nodeInitialHeight.setShaderInput( "patchLength", self.options.patchLength) self.nodeInitialHeight.setShaderInput("windDir", self.options.windDir) self.nodeInitialHeight.setShaderInput( "windSpeed", self.options.windSpeed) self.nodeInitialHeight.setShaderInput( "waveAmplitude", self.options.waveAmplitude) self.nodeInitialHeight.setShaderInput( "windDependency", self.options.windDependency) self.nodeInitialHeight.setShaderInput( "randomTex", self.randomStorageTex) self.attrInitialHeight = self.nodeInitialHeight.getAttrib(ShaderAttrib) self.heightTextures = [] for i in xrange(3): tex = Texture("Height") tex.setup2dTexture(self.options.size, self.options.size, Texture.TFloat, Texture.FRgba16) tex.setMinfilter(Texture.FTNearest) tex.setMagfilter(Texture.FTNearest) tex.setWrapU(Texture.WMClamp) tex.setWrapV(Texture.WMClamp) self.heightTextures.append(tex) # Also create the shader which updates the spectrum self.shaderUpdate = Shader.loadCompute(Shader.SLGLSL, "Shader/WaterFFT/Update.compute") self.nodeUpdate = NodePath("update") self.nodeUpdate.setShader(self.shaderUpdate) self.nodeUpdate.setShaderInput("outH0x", self.heightTextures[0]) self.nodeUpdate.setShaderInput("outH0y", self.heightTextures[1]) self.nodeUpdate.setShaderInput("outH0z", self.heightTextures[2]) self.nodeUpdate.setShaderInput("initialHeight", self.texInitialHeight) self.nodeUpdate.setShaderInput("N", LVecBase2i(self.options.size)) self.nodeUpdate.setShaderInput("time", self.ptaTime) self.attrUpdate = self.nodeUpdate.getAttrib(ShaderAttrib) # Create 3 FFTs self.fftX = GPUFFT(self.options.size, self.heightTextures[0], self.options.normalizationFactor) self.fftY = GPUFFT(self.options.size, self.heightTextures[1], self.options.normalizationFactor) self.fftZ = GPUFFT(self.options.size, self.heightTextures[2], self.options.normalizationFactor) self.combineNode = NodePath("Combine") self.combineNode.setShader(self.combineShader) self.combineNode.setShaderInput( "displacementX", self.fftX.getResultTexture()) self.combineNode.setShaderInput( "displacementY", self.fftY.getResultTexture()) self.combineNode.setShaderInput( "displacementZ", self.fftZ.getResultTexture()) self.combineNode.setShaderInput("normalDest", self.normalTex) self.combineNode.setShaderInput( "displacementDest", self.displacementTex) self.combineNode.setShaderInput( "N", LVecBase2i(self.options.size)) self.combineNode.setShaderInput( "choppyScale", self.options.choppyScale) self.combineNode.setShaderInput( "gridLength", self.options.patchLength) # Store only the shader attrib as this is way faster self.attrCombine = self.combineNode.getAttrib(ShaderAttrib)