def __init__(self): load_prc_file_data("", """ textures-power-2 none window-type offscreen win-size 100 100 gl-coordinate-system default notify-level-display error print-pipe-types #f """) ShowBase.__init__(self) dest_tex = Texture() dest_tex.setup_2d_texture(2048, 2048, Texture.T_unsigned_byte, Texture.F_rgba8) cshader = Shader.load_compute(Shader.SL_GLSL, "grain.compute.glsl") node = NodePath("") node.set_shader(cshader) node.set_shader_input("DestTex", dest_tex) attr = node.get_attrib(ShaderAttrib) self.graphicsEngine.dispatch_compute( (2048 // 16, 2048 // 16, 1), attr, self.win.get_gsg()) base.graphicsEngine.extract_texture_data(dest_tex, base.win.get_gsg()) # Convert to single channel img = PNMImage(2048, 2048, 1, 255) dest_tex.store(img) img.set_num_channels(1) tex = Texture() tex.load(img) tex.write("grain.txo.pz")
def __init__(self): load_prc_file_data( "", """ textures-power-2 none window-type offscreen win-size 100 100 gl-coordinate-system default notify-level-display error print-pipe-types #f """) ShowBase.__init__(self) dest_tex = Texture() dest_tex.setup_2d_texture(2048, 2048, Texture.T_unsigned_byte, Texture.F_rgba8) cshader = Shader.load_compute(Shader.SL_GLSL, "grain.compute.glsl") node = NodePath("") node.set_shader(cshader) node.set_shader_input("DestTex", dest_tex) attr = node.get_attrib(ShaderAttrib) self.graphicsEngine.dispatch_compute((2048 // 16, 2048 // 16, 1), attr, self.win.get_gsg()) base.graphicsEngine.extract_texture_data(dest_tex, base.win.get_gsg()) # Convert to single channel img = PNMImage(2048, 2048, 1, 255) dest_tex.store(img) img.set_num_channels(1) tex = Texture() tex.load(img) tex.write("grain.txo.pz")
def filter_cubemap(orig_pth): if not os.path.isdir("Filtered/"): os.makedirs("Filtered/") # Copy original cubemap for i in range(6): shutil.copyfile(orig_pth.replace("#", str(i)), "Filtered/0-" + str(i) + ".png") mip = 0 while True: print("Filtering mipmap", mip) mip += 1 pth = "Filtered/" + str(mip - 1) + "-#.png" dst_pth = "Filtered/" + str(mip) + "-#.png" first_img = load_nth_face(pth, 0) size = first_img.get_x_size() // 2 if size < 1: break blur_size = size * 0.002 blur_size += mip * 0.85 blur_size = int(blur_size) effective_size = size + 2 * blur_size faces = [load_nth_face(pth, i) for i in range(6)] cubemap = loader.loadCubeMap(pth) node = NodePath("") node.set_shader(compute_shader) node.set_shader_input("SourceCubemap", cubemap) node.set_shader_input("size", size) node.set_shader_input("blurSize", blur_size) node.set_shader_input("effectiveSize", effective_size) final_img = PNMImage(size, size, 3) for i in range(6): face_dest = dst_pth.replace("#", str(i)) dst = Texture("Face-" + str(i)) dst.setup_2d_texture(effective_size, effective_size, Texture.T_float, Texture.F_rgba16) # Execute compute shader node.set_shader_input("faceIndex", i) node.set_shader_input("DestTex", dst) attr = node.get_attrib(ShaderAttrib) base.graphicsEngine.dispatch_compute(( (effective_size+15) // 16, (effective_size+15) // 16, 1), attr, base.win.get_gsg()) base.graphicsEngine.extract_texture_data(dst, base.win.get_gsg()) img = PNMImage(effective_size, effective_size, 3) dst.store(img) img.gaussian_filter(blur_size) final_img.copy_sub_image(img, 0, 0, blur_size, blur_size, size, size) final_img.write(face_dest)
def exec_compute_shader(self, shader_obj, shader_inputs, exec_size, workgroup_size=(16, 16, 1)): """ Executes a compute shader. The shader object should be a shader loaded with Shader.load_compute, the shader inputs should be a dict where the keys are the names of the shader inputs and the values are the inputs. The workgroup_size has to match the size defined in the compute shader """ ntx = int(math.ceil(exec_size[0] / workgroup_size[0])) nty = int(math.ceil(exec_size[1] / workgroup_size[1])) ntz = int(math.ceil(exec_size[2] / workgroup_size[2])) nodepath = NodePath("shader") nodepath.set_shader(shader_obj) nodepath.set_shader_inputs(**shader_inputs) attr = nodepath.get_attrib(ShaderAttrib) Globals.base.graphicsEngine.dispatch_compute( (ntx, nty, ntz), attr, Globals.base.win.gsg)
def debug3DTexture(self, tex, dest): """ Writes a 3D Texture to disk """ effectiveHeight = (tex.getYSize() + 1) * (tex.getZSize()) effectiveHeight -= 1 effectiveWidth = (tex.getXSize() + 1) * 3 # effectiveHeight = tex.getYSize() effectiveWidth = tex.getXSize() self.debug("Texture Size =", tex.getXSize(), "x", tex.getYSize(), "x", tex.getZSize()) self.debug( "EffectiveHeight = ", effectiveHeight, "Layers=", tex.getZSize()) store = Texture("store") store.setup2dTexture( effectiveWidth, effectiveHeight, Texture.TFloat, Texture.FRgba16) TextureCleaner.clearTexture(store, Vec4(1, 0, 1, 1)) # Create a dummy node and apply the shader to it shader = BetterShader.loadCompute("Shader/Write3DTexture.compute") dummy = NodePath("dummy") dummy.setShader(shader) dummy.setShaderInput("source", tex) dummy.setShaderInput("height", tex.getYSize() + 1) dummy.setShaderInput("width", tex.getXSize() + 1) dummy.setShaderInput("layerCount", tex.getZSize()) dummy.setShaderInput("destination", store) # Retrieve the underlying ShaderAttrib sattr = dummy.get_attrib(ShaderAttrib) # Dispatch the compute shader, right now! Globals.base.graphicsEngine.dispatch_compute( (tex.getXSize() / 16, tex.getYSize() / 16, tex.getZSize()), sattr, Globals.base.win.get_gsg()) Globals.base.graphicsEngine.extract_texture_data( store, Globals.base.win.getGsg()) store.write(dest)
def exec_compute_shader(self, shader_obj, shader_inputs, exec_size, workgroup_size=(16, 16, 1)): """ Executes a compute shader. The shader object should be a shader loaded with Shader.load_compute, the shader inputs should be a dict where the keys are the names of the shader inputs and the values are the inputs. The workgroup_size has to match the size defined in the compute shader """ ntx = int(math.ceil(exec_size[0] / workgroup_size[0])) nty = int(math.ceil(exec_size[1] / workgroup_size[1])) ntz = int(math.ceil(exec_size[2] / workgroup_size[2])) nodepath = NodePath("shader") nodepath.set_shader(shader_obj) for key, val in iteritems(shader_inputs): nodepath.set_shader_input(key, val) attr = nodepath.get_attrib(ShaderAttrib) Globals.base.graphicsEngine.dispatch_compute( (ntx, nty, ntz), attr, Globals.base.win.gsg)
def debug3DTexture(self, tex, dest): """ Writes a 3D Texture to disk """ effectiveHeight = (tex.getYSize()+1) * (tex.getZSize()) effectiveHeight -= 1 effectiveWidth = (tex.getXSize()+1)*3 effectiveHeight = tex.getYSize() effectiveWidth = tex.getXSize() self.debug("Texture Size =",tex.getXSize(),"x",tex.getYSize(),"x",tex.getZSize()) self.debug("EffectiveHeight = ", effectiveHeight, "Layers=",tex.getZSize()) store = Texture("store") store.setup2dTexture( effectiveWidth, effectiveHeight, Texture.TFloat, Texture.FRgba16) TextureCleaner.clearTexture(store, Vec4(1,0,1,1)) # Create a dummy node and apply the shader to it shader = BetterShader.loadCompute("Shader/Write3DTexture.compute") dummy = NodePath("dummy") dummy.setShader(shader) dummy.setShaderInput("source", tex) dummy.setShaderInput("height", tex.getYSize() + 1) dummy.setShaderInput("width", tex.getXSize() + 1) dummy.setShaderInput("layerCount", tex.getZSize()) dummy.setShaderInput("destination", store) # Retrieve the underlying ShaderAttrib sattr = dummy.get_attrib(ShaderAttrib) # Dispatch the compute shader, right now! Globals.base.graphicsEngine.dispatch_compute( (tex.getXSize() / 16, tex.getYSize() / 16, tex.getZSize()), sattr, base.win.get_gsg()) Globals.base.graphicsEngine.extract_texture_data( store, Globals.base.win.getGsg()) store.write(dest)
def clearTexture(self, tex, clearColor): """ Clears a texture object """ if tex.getZSize() > 1: print "TextureCleaner: 3D Textures not supported (yet!)" return else: w, h, d = tex.getXSize(), tex.getYSize(), tex.getZSize() dispatchW = (w + 15) / 16 dispatchH = (h + 15) / 16 shader = self._createClearShader() dummy = NodePath("dummy") dummy.setShader(shader) dummy.setShaderInput("layers", d) dummy.setShaderInput("destination", tex) dummy.setShaderInput("clearColor", clearColor) sattr = dummy.get_attrib(ShaderAttrib) # Dispatch the compute shader, right now! Globals.base.graphicsEngine.dispatch_compute( (dispatchW, dispatchH, 1), sattr, Globals.base.win.get_gsg())
def clearTexture(self, tex, clearColor): """ Clears a texture object """ if tex.getZSize() > 1: print "TextureCleaner: 3D Textures not supported (yet!)" return else: if self.ClearShader is None: self.ClearShader = self._createClearShader() w, h, d = tex.getXSize(), tex.getYSize(), tex.getZSize() dispatchW = (w + 15) / 16 dispatchH = (h + 15) / 16 dummy = NodePath("dummy") dummy.setShader(self.ClearShader) dummy.setShaderInput("layers", d) dummy.setShaderInput("destination", tex) dummy.setShaderInput("clearColor", clearColor) sattr = dummy.get_attrib(ShaderAttrib) # Dispatch the compute shader, right now! Globals.base.graphicsEngine.dispatch_compute( (dispatchW, dispatchH, 1), sattr, Globals.base.win.get_gsg())
class WaterManager: """ Simple wrapper around WaterDisplacement which combines 3 displacement maps into one, and also generates a normal map """ def __init__(self, water_options): self.options = water_options self.options.size = 512 self.options.wind_dir.normalize() self.options.wave_amplitude *= 1e-7 self.displacement_tex = Texture("Displacement") self.displacement_tex.setup_2d_texture( self.options.size, self.options.size, Texture.TFloat, Texture.FRgba16) self.normal_tex = Texture("Normal") self.normal_tex.setup_2d_texture( self.options.size, self.options.size, Texture.TFloat, Texture.FRgba16) self.combine_shader = Shader.load_compute(Shader.SLGLSL, "/$$rp/rpcore/water/shader/combine.compute") self.pta_time = PTAFloat.emptyArray(1) # Create a gaussian random texture, as shaders aren't well suited # for that setRandomSeed(523) self.random_storage = PNMImage(self.options.size, self.options.size, 4) self.random_storage.setMaxval((2 ** 16) - 1) for x in range(self.options.size): for y in range(self.options.size): rand1 = self._get_gaussian_random() / 10.0 + 0.5 rand2 = self._get_gaussian_random() / 10.0 + 0.5 self.random_storage.setXel(x, y, float(rand1), float(rand2), 0) self.random_storage.setAlpha(x, y, 1.0) self.random_storage_tex = Texture("RandomStorage") self.random_storage_tex.load(self.random_storage) self.random_storage_tex.set_format(Texture.FRgba16) self.random_storage_tex.set_minfilter(Texture.FTNearest) self.random_storage_tex.set_magfilter(Texture.FTNearest) # Create the texture wwhere the intial height (H0 + Omega0) is stored. self.tex_initial_height = Texture("InitialHeight") self.tex_initial_height.setup_2d_texture( self.options.size, self.options.size, Texture.TFloat, Texture.FRgba16) self.tex_initial_height.set_minfilter(Texture.FTNearest) self.tex_initial_height.set_magfilter(Texture.FTNearest) # Create the shader which populates the initial height texture self.shader_initial_height = Shader.load_compute(Shader.SLGLSL, "/$$rp/rpcore/water/shader/initial_height.compute") self.node_initial_height = NodePath("initialHeight") self.node_initial_height.set_shader(self.shader_initial_height) self.node_initial_height.set_shader_input("dest", self.tex_initial_height) self.node_initial_height.set_shader_input( "N", LVecBase2i(self.options.size)) self.node_initial_height.set_shader_input( "patchLength", self.options.patch_length) self.node_initial_height.set_shader_input("windDir", self.options.wind_dir) self.node_initial_height.set_shader_input( "windSpeed", self.options.wind_speed) self.node_initial_height.set_shader_input( "waveAmplitude", self.options.wave_amplitude) self.node_initial_height.set_shader_input( "windDependency", self.options.wind_dependency) self.node_initial_height.set_shader_input( "randomTex", self.random_storage_tex) self.attr_initial_height = self.node_initial_height.get_attrib(ShaderAttrib) self.height_textures = [] for i in range(3): tex = Texture("Height") tex.setup_2d_texture(self.options.size, self.options.size, Texture.TFloat, Texture.FRgba16) tex.set_minfilter(Texture.FTNearest) tex.set_magfilter(Texture.FTNearest) tex.set_wrap_u(Texture.WMClamp) tex.set_wrap_v(Texture.WMClamp) self.height_textures.append(tex) # Also create the shader which updates the spectrum self.shader_update = Shader.load_compute(Shader.SLGLSL, "/$$rp/rpcore/water/shader/update.compute") self.node_update = NodePath("update") self.node_update.set_shader(self.shader_update) self.node_update.set_shader_input("outH0x", self.height_textures[0]) self.node_update.set_shader_input("outH0y", self.height_textures[1]) self.node_update.set_shader_input("outH0z", self.height_textures[2]) self.node_update.set_shader_input("initialHeight", self.tex_initial_height) self.node_update.set_shader_input("N", LVecBase2i(self.options.size)) self.node_update.set_shader_input("time", self.pta_time) self.attr_update = self.node_update.get_attrib(ShaderAttrib) # Create 3 FFTs self.fftX = GPUFFT(self.options.size, self.height_textures[0], self.options.normalization_factor) self.fftY = GPUFFT(self.options.size, self.height_textures[1], self.options.normalization_factor) self.fftZ = GPUFFT(self.options.size, self.height_textures[2], self.options.normalization_factor) self.combine_node = NodePath("Combine") self.combine_node.set_shader(self.combine_shader) self.combine_node.set_shader_input( "displacementX", self.fftX.get_result_texture()) self.combine_node.set_shader_input( "displacementY", self.fftY.get_result_texture()) self.combine_node.set_shader_input( "displacementZ", self.fftZ.get_result_texture()) self.combine_node.set_shader_input("normalDest", self.normal_tex) self.combine_node.set_shader_input( "displacementDest", self.displacement_tex) self.combine_node.set_shader_input( "N", LVecBase2i(self.options.size)) self.combine_node.set_shader_input( "choppyScale", self.options.choppy_scale) self.combine_node.set_shader_input( "gridLength", self.options.patch_length) # Store only the shader attrib as this is way faster self.attr_combine = self.combine_node.get_attrib(ShaderAttrib) def _get_gaussian_random(self): """ Returns a gaussian random number """ u1 = generateRandom() u2 = generateRandom() if u1 < 1e-6: u1 = 1e-6 return sqrt(-2 * log(u1)) * cos(2 * pi * u2) def setup(self): """ Setups the manager """ Globals.base.graphicsEngine.dispatch_compute( (self.options.size // 16, self.options.size // 16, 1), self.attr_initial_height, Globals.base.win.get_gsg()) def get_displacement_texture(self): """ Returns the displacement texture, storing the 3D Displacement in the RGB channels """ return self.displacement_tex def get_normal_texture(self): """ Returns the normal texture, storing the normal in world space in the RGB channels """ return self.normal_tex def update(self): """ Updates the displacement / normal map """ self.pta_time[0] = 1 + Globals.clock.get_frame_time() * self.options.time_scale Globals.base.graphicsEngine.dispatch_compute( (self.options.size // 16, self.options.size // 16, 1), self.attr_update, Globals.base.win.get_gsg()) self.fftX.execute() self.fftY.execute() self.fftZ.execute() # Execute the shader which combines the 3 displacement maps into # 1 displacement texture and 1 normal texture. We could use dFdx in # the fragment shader, however that gives no accurate results as # dFdx returns the same value for a 2x2 pixel block Globals.base.graphicsEngine.dispatch_compute( (self.options.size // 16, self.options.size // 16, 1), self.attr_combine, Globals.base.win.get_gsg())