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")
Exemple #2
0
    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")
Exemple #3
0
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())
Exemple #10
0
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())