{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreicm5vyca6xkpoiy3ee522qjjzfshc7rvqhy3keyma3poxjgxfl64u",
    "uri": "at://did:plc:dxjzgxe7cvirxkwfjr2tjspt/app.bsky.feed.post/3mi5aeburnve2"
  },
  "path": "/t/an-attempt-at-vulkan/49433?page=3#post_43",
  "publishedAt": "2026-03-26T06:09:36.000Z",
  "site": "https://hub.jmonkeyengine.org",
  "tags": [
    "@Override"
  ],
  "textContent": "\n    package com.jme3.lwjgl.test;\n\n    import com.jme3.app.LegacyApplication;\n    import com.jme3.asset.TextureKey;\n    import com.jme3.asset.plugins.ClasspathLocator;\n    import com.jme3.export.binary.BinaryImporter;\n    import com.jme3.input.FlyByCamera;\n    import com.jme3.material.Material;\n    import com.jme3.material.plugins.J3MLoader;\n    import com.jme3.math.ColorRGBA;\n    import com.jme3.math.Vector3f;\n    import com.jme3.renderer.Camera;\n    import com.jme3.renderer.ViewPort;\n    import com.jme3.scene.Geometry;\n    import com.jme3.scene.Node;\n    import com.jme3.scene.Spatial;\n    import com.jme3.scene.shape.Box;\n    import com.jme3.system.AppSettings;\n    import com.jme3.system.JmeSystem;\n    import com.jme3.system.VulkanSystemDelegate;\n    import com.jme3.texture.Image;\n    import com.jme3.texture.Texture;\n    import com.jme3.texture.Texture2D;\n    import com.jme3.texture.image.ColorSpace;\n    import com.jme3.util.BufferUtils;\n    import java.awt.image.BufferedImage;\n    import java.io.File;\n    import java.nio.ByteBuffer;\n    import javax.imageio.ImageIO;\n\n    public class VulkanMultiSetTextureTest extends LegacyApplication {\n\n        //\"Common/MatDefs/Aurora/Aurora.j3md\"\n        //\"Common/MatDefs/Misc/VKUnshaded.j3md\"\n        private static final String MAT_DEF = \"Common/MatDefs/Misc/VKUnshaded.j3md\";\n\n        private final Node rootNode = new Node(\"Root\");\n        private ViewPort viewPort;\n        private Camera cam;\n        private FlyByCamera flyCam;\n\n        private float fpsTime = 0f;\n        private int fpsFrames = 0;\n\n        // 新增:保存测试几何体,update 时可拿到 material\n        private Geometry testGeom;\n\n        @Override\n        public void initialize() {\n            super.initialize();\n\n            assetManager.registerLocator(\"/\", ClasspathLocator.class);\n            assetManager.registerLoader(J3MLoader.class, \"j3md\", \"j3m\");\n            assetManager.registerLoader(BinaryImporter.class, \"j3o\");\n\n\n            cam = getCamera();\n            viewPort = renderManager.createMainView(\"Main\", cam);\n            viewPort.setBackgroundColor(ColorRGBA.DarkGray);\n            viewPort.attachScene(rootNode);\n\n            cam.setLocation(new Vector3f(0f, 0f, 20f));\n            cam.setFrustumFar(1000f);\n\n            flyCam = new FlyByCamera(cam);\n            flyCam.setMoveSpeed(30f);\n            flyCam.setDragToRotate(true);\n            flyCam.registerWithInput(inputManager);\n\n            Box box = new Box(2f, 2f, 2f);\n            testGeom = new Geometry(\"MultiSetBox\", box);\n\n            Material mat = new Material(assetManager, MAT_DEF);\n\n            Texture2D extra = loadPngFromFileNoJmeLoader(\"F:/JME/jmonkeyengine/jme3-core/src/main/resources/Common/Textures/MissingMaterial.png\", true);\n            if (extra != null) {\n                mat.setTexture(\"ColorMap\", extra);\n                //mat.setTexture(\"ExtraTex\", extra);\n            }\n    //        assetManager.registerLocator(\"F:/AYA2022年10月16日/Jme\", com.jme3.asset.plugins.FileLocator.class);\n    //        Spatial model = assetManager.loadModel(\"kasolia.j3o\");\n    //        model.setMaterial(mat);\n    //        rootNode.attachChild(model);\n            testGeom.setMaterial(mat);\n            rootNode.attachChild(testGeom);\n\n            System.out.println(\"[App] mat.ExtraTex=\" + mat.getParam(\"ExtraTex\"));\n            System.out.println(\"[App] init done. children=\" + rootNode.getQuantity()\n                    + \", ExtraTex=\" + (extra != null));\n        }\n\n        private Texture loadTexture(String path) {\n            try {\n                TextureKey key = new TextureKey(path, false);\n                Texture tex = assetManager.loadTexture(key);\n                tex.setAnisotropicFilter(4);\n                return tex;\n            } catch (Exception e) {\n                System.err.println(\"[Warn] texture load failed: \" + path + \" -> \" + e.getMessage());\n                return null;\n            }\n        }\n\n        @Override\n        public void update() {\n            super.update();\n\n            float tpf = timer.getTimePerFrame();\n\n            fpsTime += tpf;\n            fpsFrames++;\n            if (fpsTime >= 1.0f) {\n                int fps = Math.round(fpsFrames / fpsTime);\n                fpsTime = 0f;\n                fpsFrames = 0;\n                if (context != null) {\n                    context.setTitle(\"Vulkan MultiSet Texture Test | FPS: \" + fps);\n                }\n            }\n\n            rootNode.updateLogicalState(tpf);\n            rootNode.updateGeometricState();\n\n            if (context != null && context.isRenderable()) {\n                renderManager.render(tpf, true);\n            }\n        }\n\n        private static Texture2D loadPngFromFileNoJmeLoader(String absPath, boolean flipY) {\n            try {\n                BufferedImage bi = ImageIO.read(new File(absPath));\n                if (bi == null) {\n                    return null;\n                }\n\n                int w = bi.getWidth();\n                int h = bi.getHeight();\n                ByteBuffer buf = BufferUtils.createByteBuffer(w * h * 4);\n\n                for (int row = 0; row < h; row++) {\n                    int y = flipY ? (h - 1 - row) : row; // 关键:按需翻转Y\n                    for (int x = 0; x < w; x++) {\n                        int argb = bi.getRGB(x, y);\n                        byte a = (byte) ((argb >> 24) & 0xFF);\n                        byte r = (byte) ((argb >> 16) & 0xFF);\n                        byte g = (byte) ((argb >> 8) & 0xFF);\n                        byte b = (byte) (argb & 0xFF);\n                        buf.put(r).put(g).put(b).put(a);\n                    }\n                }\n                buf.flip();\n\n                Image img = new Image(Image.Format.RGBA8, w, h, buf, null, ColorSpace.Linear);\n                Texture2D tex = new Texture2D(img);\n                tex.setMinFilter(com.jme3.texture.Texture.MinFilter.BilinearNoMipMaps);\n                tex.setMagFilter(com.jme3.texture.Texture.MagFilter.Bilinear);\n                tex.setWrap(com.jme3.texture.Texture.WrapMode.Repeat);\n                return tex;\n            } catch (Exception e) {\n                e.printStackTrace();\n                return null;\n            }\n        }\n\n        public static void main(String[] args) {\n            JmeSystem.setSystemDelegate(new VulkanSystemDelegate());\n\n            AppSettings s = new AppSettings(true);\n            s.setCustomRenderer(com.jme3.renderer.vulkan.context.LwjglVulkanContext.class);\n            s.setWidth(1920);\n            s.setHeight(1080);\n            s.setTitle(\"Vulkan MultiSet Texture Test\");\n\n            VulkanMultiSetTextureTest app = new VulkanMultiSetTextureTest();\n            app.setSettings(s);\n            app.start();\n        }\n    }\n\n\n\nMade significant progress\nI attempted to modify a file named VKUnshaded.j3md for testing purposes.\n\n\n    #version 450\n    #define VULKAN_NATIVE 1\n\n    #import \"Common/ShaderLib/VKGLSLCompat.glsllib\"\n    #import \"Common/ShaderLib/VKSkinning.glsllib\"\n    // #import \"Common/ShaderLib/VKInstancing.glsllib\"   // 先禁用,避免矩阵UBO冲突\n    #import \"Common/ShaderLib/VKMorphAnim.glsllib\"\n\n    layout(location = 0) in vec3 inPosition;\n    layout(location = 1) in vec2 inTexCoord;\n    layout(location = 2) in vec2 inTexCoord2;\n    layout(location = 3) in vec4 inColor;\n\n    layout(location = 0) out vec2 texCoord1;\n    layout(location = 1) out vec2 texCoord2;\n    layout(location = 2) out vec4 vertColor;\n\n    // 与 FS 对齐的主 UBO\n    layout(set = 0, binding = 0, std140) uniform JmeUniforms {\n        mat4 g_WorldViewProjectionMatrix;\n        vec4 m_Color;\n        vec4 g_Resolution;\n        vec4 g_Mouse;\n        vec4 g_Time;\n    };\n\n    #ifdef HAS_POINTSIZE\n    layout(set = 0, binding = 3) uniform PointSizeUbo {\n        float m_PointSize;\n    };\n    #endif\n\n    #if defined(HAS_COLORMAP) || (defined(HAS_LIGHTMAP) && !defined(SEPARATE_TEXCOORD))\n        #define NEED_TEXCOORD1\n    #endif\n\n    void main() {\n    #ifdef NEED_TEXCOORD1\n        texCoord1 = inTexCoord;\n    #endif\n\n    #ifdef SEPARATE_TEXCOORD\n        texCoord2 = inTexCoord2;\n    #endif\n\n    #ifdef HAS_VERTEXCOLOR\n        vertColor = inColor;\n    #else\n        vertColor = vec4(1.0);\n    #endif\n\n    #ifdef HAS_POINTSIZE\n        gl_PointSize = m_PointSize;\n    #endif\n\n        vec4 modelSpacePos = vec4(inPosition, 1.0);\n\n        // Phase-1:先注释,后续分批接回\n        // #ifdef NUM_MORPH_TARGETS\n        //     Morph_Compute(modelSpacePos);\n        // #endif\n        //\n        // #ifdef NUM_BONES\n        //     Skinning_Compute(modelSpacePos);\n        // #endif\n\n        gl_Position = g_WorldViewProjectionMatrix * modelSpacePos;\n    }\n\n\n\n\n    #version 450\n    #define VULKAN_NATIVE 1\n    #import \"Common/ShaderLib/VKGLSLCompat.glsllib\"\n\n    #if defined(HAS_GLOWMAP) || defined(HAS_COLORMAP) || (defined(HAS_LIGHTMAP) && !defined(SEPARATE_TEXCOORD))\n        #define NEED_TEXCOORD1\n    #endif\n\n    // 恢复基础材质/全局参数块(与 VS 统一 set/binding)\n    layout(set = 0, binding = 0, std140) uniform JmeUniforms {\n        mat4 g_WorldViewProjectionMatrix; // FS 不用,但保持跨阶段布局一致\n        vec4 m_Color;                     // HAS_COLOR 时使用\n        vec4 g_Resolution;\n        vec4 g_Mouse;\n        vec4 g_Time;\n    };\n\n    layout(set = 0, binding = 1) uniform sampler2D m_ColorMap;\n    layout(set = 0, binding = 2) uniform sampler2D m_LightMap;\n\n    #if defined(DISCARD_ALPHA)\n    layout(set = 0, binding = 3, std140) uniform AlphaParams {\n        float m_AlphaDiscardThreshold;\n    };\n    #endif\n\n    #ifdef DESATURATION\n    layout(set = 0, binding = 4, std140) uniform DesaturationParams {\n        float m_DesaturationValue;\n    };\n    #endif\n\n    layout(location = 0) in vec2 texCoord1;\n    layout(location = 1) in vec2 texCoord2;\n    layout(location = 2) in vec4 vertColor;\n\n    layout(location = 0) out vec4 fragColor;\n\n    void main() {\n        vec4 color = vec4(1.0);\n\n    #ifdef HAS_COLORMAP\n        color *= texture(m_ColorMap, texCoord1);\n    #endif\n\n    #ifdef HAS_VERTEXCOLOR\n        color *= vertColor;\n    #endif\n\n    #ifdef HAS_COLOR\n        color *= m_Color;\n    #endif\n\n    #ifdef HAS_LIGHTMAP\n        #ifdef SEPARATE_TEXCOORD\n            color.rgb *= texture(m_LightMap, texCoord2).rgb;\n        #else\n            color.rgb *= texture(m_LightMap, texCoord1).rgb;\n        #endif\n    #endif\n\n    #if defined(DISCARD_ALPHA)\n        if (color.a < m_AlphaDiscardThreshold) {\n            discard;\n        }\n    #endif\n\n    #ifdef DESATURATION\n        vec3 gray = vec3(dot(vec3(0.2126, 0.7152, 0.0722), color.rgb));\n        color.rgb = mix(color.rgb, gray, m_DesaturationValue);\n    #endif\n\n        fragColor = color;\n    }\n\n\n\n\n        @Override\n        public void updateBufferData(VertexBuffer vb) {\n            if (vb == null) {\n                return;\n            }\n\n            try {\n                runtime.invalidateMeshGpuByVertexBuffer(vb);\n            } catch (Throwable t) {\n                // 兜底全量失效\n                LOGGER.log(Level.WARNING, \"[VKRenderer] precise invalidate failed, fallback to all, type=\"\n                        + vb.getBufferType(), t);\n                runtime.invalidateAllMeshGpu();\n            }\n\n            if (LOGGER.isLoggable(Level.FINE)) {\n                LOGGER.fine(\"[VKRenderer] updateBufferData(VertexBuffer) -> precise invalidate, type=\"\n                        + vb.getBufferType());\n            }\n        }\n\n\nToday, the function “updateBufferData” was also completed.\n\nI am very satisfied with the current progress. I believe it won’t be long before the first version is released and then we can recruit volunteers to test it.",
  "title": "An attempt at Vulkan"
}