Custom Per Vertex Data for Shader

Hello out there! I hope my Question isn’t too dumb but i really can’t figure it out :confused:

I need 3 special Values in my Shader: 2 Floats for a Position and 1 Integer or Float for a Size. That Values would be different for each Vertex. Maybe i could “missuse” the vertex normal data for that, but thats not really a good solution i think.

As i understand it, vertex data is set by the Mesh.setBuffer(Type, components,Buffer) Method. “in[Type]” should then give the shader access to the data. So i could choose type “TexCoord” for my 2 position floats and the type “Size” for my size value.
But this seems to only work for inPosition,inNormal,inColor,inTexCoord. I tried to create a Buffer of the Type Size and then acces it in the Vertex Shader like its done with inColor but it doesen’t work.

[java]mesh.setBuffer(VertexBuffer.Type.Size, 1, sizeBuffer);[/java]

(.j3md, see lines 8 and 30)
[java]
MaterialDef Unshaded {

MaterialParameters {
    Texture2D ColorMap
    Texture2D LightMap
    Color Color ( Color )
    Boolean VertexColor
    Boolean VertexSize
    Boolean SeparateTexCoord
    Boolean DirectViewspace
    // Texture of the glowing parts of the material
    Texture2D GlowMap
    // The glow color of the object
    Color GlowColor
}

Technique {
    VertexShader GLSL100:   MatDefs/UnshadedPointSprite.vert
    FragmentShader GLSL100: MatDefs/UnshadedPointSprite.frag

    WorldParameters {
        WorldViewProjectionMatrix
    }

    Defines {
        SEPARATE_TEXCOORD : SeparateTexCoord
        HAS_COLORMAP : ColorMap
        HAS_LIGHTMAP : LightMap
        HAS_VERTEXCOLOR : VertexColor
        HAS_VERTEXSIZE : VertexSize
        HAS_COLOR : Color
        IS_DIRECTVIEWSPACE: DirectViewspace
    }
}

  Technique PreNormalPass {

        VertexShader GLSL100 :   Common/MatDefs/SSAO/normal.vert
        FragmentShader GLSL100 : Common/MatDefs/SSAO/normal.frag

        WorldParameters {
            WorldViewProjectionMatrix
            WorldViewMatrix
            NormalMatrix
        }

        RenderState {

        }

    }


Technique Glow {

    VertexShader GLSL100:   Common/MatDefs/Misc/Unshaded.vert
    FragmentShader GLSL100: Common/MatDefs/Light/Glow.frag

    WorldParameters {
        WorldViewProjectionMatrix
    }

    Defines {
        HAS_GLOWMAP : GlowMap
        HAS_GLOWCOLOR : GlowColor
        HAS_COLORMAP // Must be passed so that Unshaded.vert exports texCoord.
    }
}

}
[/java]

(.vert see lines 18-21)
[java]uniform mat4 g_WorldViewProjectionMatrix;
attribute vec3 inPosition;
varying vec4 tempPos;
#if defined(HAS_COLORMAP) || (defined(HAS_LIGHTMAP) && !defined(SEPARATE_TEXCOORD))
#define NEED_TEXCOORD1
#endif

#ifdef NEED_TEXCOORD1
attribute vec2 inTexCoord;
varying vec2 texCoord1;
#endif

#ifdef SEPARATE_TEXCOORD
attribute vec2 inTexCoord2;
varying vec2 texCoord2;
#endif

#ifdef HAS_VERTEXCOLOR
attribute vec4 inColor;
varying vec4 vertColor;
#endif

#ifdef HAS_VERTEXSIZE
attribute float inSize;
varying float vertSize;
#endif

void main(){
#ifdef NEED_TEXCOORD1
texCoord1 = inTexCoord;
#endif

#ifdef SEPARATE_TEXCOORD
    texCoord2 = inTexCoord2;
#endif

#ifdef HAS_VERTEXCOLOR
    vertColor = inColor;
#endif

gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);

gl_PointSize = 250.0 / gl_Position.w;
tempPos = gl_Position;

}[/java]

(.frag see lines 36-38 and 48)
[java]uniform vec4 m_Color;
varying vec4 tempPos;

const float C = 1.0;

const float far = 2^40;
const float offset = 1.0;

const int k = 32;//bits of depthbuffer (16/24/32)
const float zn = 1.0;
const float zf = 2^40;
const float K = ( (2^k) - 1 ) / log( zf / zn );

#if defined(HAS_GLOWMAP) || defined(HAS_COLORMAP) || (defined(HAS_LIGHTMAP) && !defined(SEPARATE_TEXCOORD))
#define NEED_TEXCOORD1
#endif

#ifdef HAS_COLORMAP
uniform sampler2D m_ColorMap;
#endif

#ifdef NEED_TEXCOORD1
varying vec2 texCoord1;
#endif

#ifdef HAS_LIGHTMAP
uniform sampler2D m_LightMap;
#ifdef SEPARATE_TEXCOORD
varying vec2 texCoord2;
#endif
#endif

#ifdef HAS_VERTEXCOLOR
varying vec4 vertColor;
#endif

#ifdef HAS_VERTEXSIZE
varying float vertSize;
#endif

void main(){
vec4 color = vec4(1.0);

#ifdef HAS_VERTEXCOLOR
    color *= vertColor;
#endif

#ifdef HAS_COLORMAP
color = texture2D(m_ColorMap,vec2(vertSizegl_PointCoord.x,vertSize*(1-gl_PointCoord.y)));
#endif

#ifdef HAS_COLOR
    color *= m_Color;
#endif

#ifdef HAS_LIGHTMAP
    #ifdef SEPARATE_TEXCOORD
        color.rgb *= texture2D(m_LightMap, texCoord2).rgb;
    #else
        color.rgb *= texture2D(m_LightMap, texCoord1).rgb;
    #endif
#endif

gl_FragColor = color;
}[/java]

When i execute the shader always a 0 is passed as size although i set a 1.0f in the size buffer.
Am i missing something?

You declare vertSize as a varying and never set a value, but then try and use it here:

color = texture2D(m_ColorMap,vec2(vertSizegl_PointCoord.x,vertSize*(1-gl_PointCoord.y)));

Assuming the buffer is being passed in correctly and is really associated with inSize… you’ll want to assign that value to vertSize in the vertex shader

1 Like

This thread may help you:
http://hub.jmonkeyengine.org/forum/topic/vertex-colour-in-a-shader/

1 Like

Oh noo, of course! I really missed that. Now it works!

Thank you very much!

@zarch:

Yes indeed interesting Thread. So i will use TexCoord2 then for my Custom Position Data instead of standard TexCoord :slight_smile:

@naas Silly question! What does your shader do? Love to see a screen shot of the final render.

@t0neg0d:

Not a silly question my friend :slight_smile:

Since i learned how every polygon in a scene is rendered 60 times a second i always thought a big part of this rendering efford is wasted because large parts of the scene do barely change most of the time. So why don’t we render them only one time and then use the result more often?
To do this i want to try to make excessive use of render to texture for rendering far away or static objects.
The rendered textures should then be displayed as point sprites. But i want to render multiple objects on one single texture so we don’t end up using 100s of textures with only small resolutions. To let a pointSprite show only a certain area of a texture which contains the single object the sprite is representing i need this shader.

I will post a picture of it if i’m able to render different objects on different sprites :slight_smile:

1 Like

@naas it sounds to me like you are talking about imposters. If you search for that term on these forums you will find a lot of discussion on them.

@zarch: yes thanks again for that hint. I wasn’t fully aware what that word means before. I only knew Billboard and Sprite. Well still i’m not certain if they all mean exactly the same :smiley:

After a little forum search It seems like what i plan to do has not been implemented yet (a automated system that handles rendering objects onto certain areas of textures and assigning them to sprites) .

Also i got aware of a problem why it seems i can’t use pointsprites like i planned: If the camera rotates in the z-axis, the sprite would have to be rotated too. But pointsprites don’t seem to be rotateable. Of course the texture can be rotated but then other problems would occour.

So i have to take real polygon billboards then. For those of course i don’t need extra texture coordinates and size values like i did with the pointsprites.

Anyways, if anybody wants to tryout pointsprites and maybe need the pointsprites to display certain areas of a texture (for animation for example) i post my pointsprite code i have so far. It also handles sizing of the sprites according to distance, and needs only one vertex per sprite (so i think its faster than sprites consisting of multiple vertexes). As mentioned before the drawback is that the sprites cannot be rotated though.

Main.java
[java]package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.VertexBuffer;
import com.jme3.scene.shape.Box;
import com.jme3.util.BufferUtils;
import java.nio.FloatBuffer;

/**

  • test

  • @author normenhansen
    */
    public class Main extends SimpleApplication {

    public static void main(String[] args) {
    Main app = new Main();
    app.start();
    }

    @Override
    public void simpleInitApp() {

     Material mat = new Material(assetManager, "MatDefs/UnshadedPointSprite.j3md");
     mat.setTexture("ColorMap",assetManager.loadTexture("Textures/TestTexture.jpg") );
     mat.setBoolean("PointSpriteCoordinates", true);
     mat.setBoolean("VertexSize", true);
     mat.getAdditionalRenderState().setPointSprite(true);
     
     Mesh pointsMesh = new Mesh();
     pointsMesh.setMode(Mesh.Mode.Points);
    
     int pointCount = 3;
    
     FloatBuffer positionBuffer = BufferUtils.createFloatBuffer(3 * pointCount);
     FloatBuffer pointTexCoordBuffer = BufferUtils.createFloatBuffer(2 * pointCount);
     FloatBuffer pointTexSizeBuffer = BufferUtils.createFloatBuffer(2 * pointCount);
     FloatBuffer pointSizeBuffer = BufferUtils.createFloatBuffer(1 * pointCount);
    
     positionBuffer.put(0).put(0).put(0);
     pointTexCoordBuffer.put(0.0f).put(0.0f);
     pointTexSizeBuffer.put(0.25f).put(0.25f);
     pointSizeBuffer.put(500f);
    
     positionBuffer.put(1).put(1).put(1);
     pointTexCoordBuffer.put(0.25f).put(0.25f);
     pointTexSizeBuffer.put(0.25f).put(0.25f);
     pointSizeBuffer.put(500f);
     
     positionBuffer.put(1).put(0).put(1);
     pointTexCoordBuffer.put(0.0f).put(0.25f);
     pointTexSizeBuffer.put(0.25f).put(0.5f);
     pointSizeBuffer.put(200f);
    
     pointsMesh.setBuffer(VertexBuffer.Type.Position, 3, positionBuffer);
     pointsMesh.setBuffer(VertexBuffer.Type.TexCoord3, 2, pointTexCoordBuffer);
     pointsMesh.setBuffer(VertexBuffer.Type.TexCoord2, 2, pointTexSizeBuffer);
     pointsMesh.setBuffer(VertexBuffer.Type.Size, 1, pointSizeBuffer);
     pointsMesh.updateBound();
     
     
     
    
     
     Geometry testPoints = new Geometry("TestPoints",pointsMesh);
     testPoints.setMaterial(mat);
     
     rootNode.attachChild(testPoints);
    

    }

    @Override
    public void simpleUpdate(float tpf) {
    //TODO: add update code
    }

    @Override
    public void simpleRender(RenderManager rm) {
    //TODO: add render code
    }
    }
    [/java]

(UnshadedPointSprite.j3md)
[java]MaterialDef Unshaded {

MaterialParameters {
    Texture2D ColorMap
    Texture2D LightMap
    Color Color ( Color )
    Boolean VertexColor
    Boolean VertexSize
    Boolean SeparateTexCoord
    Boolean PointSpriteCoordinates;
    Boolean DirectViewspace
    // Texture of the glowing parts of the material
    Texture2D GlowMap
    // The glow color of the object
    Color GlowColor
}

Technique {
    VertexShader GLSL100:   MatDefs/UnshadedPointSprite.vert
    FragmentShader GLSL100: MatDefs/UnshadedPointSprite.frag

    WorldParameters {
        WorldViewProjectionMatrix
    }

    Defines {
        SEPARATE_TEXCOORD : SeparateTexCoord
        HAS_COLORMAP : ColorMap
        HAS_LIGHTMAP : LightMap
        HAS_VERTEXCOLOR : VertexColor
        HAS_VERTEXSIZE : VertexSize
        HAS_POINTSPRITECOORDINATES : PointSpriteCoordinates
        HAS_COLOR : Color
        IS_DIRECTVIEWSPACE: DirectViewspace
    }
}

  Technique PreNormalPass {

        VertexShader GLSL100 :   Common/MatDefs/SSAO/normal.vert
        FragmentShader GLSL100 : Common/MatDefs/SSAO/normal.frag

        WorldParameters {
            WorldViewProjectionMatrix
            WorldViewMatrix
            NormalMatrix
        }

        RenderState {

        }

    }


Technique Glow {

    VertexShader GLSL100:   Common/MatDefs/Misc/Unshaded.vert
    FragmentShader GLSL100: Common/MatDefs/Light/Glow.frag

    WorldParameters {
        WorldViewProjectionMatrix
    }

    Defines {
        HAS_GLOWMAP : GlowMap
        HAS_GLOWCOLOR : GlowColor
        HAS_COLORMAP // Must be passed so that Unshaded.vert exports texCoord.
    }
}

}[/java]

(UnshadedPointSprite.vert)
[java]
uniform mat4 g_WorldViewProjectionMatrix;
attribute vec3 inPosition;

#if defined(HAS_COLORMAP) || (defined(HAS_LIGHTMAP) && !defined(SEPARATE_TEXCOORD))
#define NEED_TEXCOORD1
#endif

#ifdef NEED_TEXCOORD1
attribute vec2 inTexCoord;
varying vec2 texCoord1;
#endif

#ifdef SEPARATE_TEXCOORD
attribute vec2 inTexCoord2;
varying vec2 texCoord2;
#endif

#ifdef HAS_VERTEXCOLOR
attribute vec4 inColor;
varying vec4 vertColor;
#endif

#ifdef HAS_VERTEXSIZE
attribute float inSize;
//varying float vertSize;
#endif

#ifdef HAS_POINTSPRITECOORDINATES
//attribute vec2 inTexCoord3;
// varying vec2 pointTexCoo;

attribute vec2 inTexCoord2;
varying vec2 pointTexSize;

attribute vec2 inTexCoord3;
varying vec2 pointTexCoord;
//attribute vec2 inTexCoord2;
//
#endif

void main(){
#ifdef NEED_TEXCOORD1
texCoord1 = inTexCoord;
#endif

#ifdef SEPARATE_TEXCOORD
    texCoord2 = inTexCoord2;
#endif

#ifdef HAS_VERTEXCOLOR
    vertColor = inColor;
#endif


#ifdef HAS_POINTSPRITECOORDINATES
pointTexSize = inTexCoord2;
pointTexCoord = inTexCoord3;

// pointTexCoord = inTexCoord2;
#endif

gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);

gl_PointSize = inSize / gl_Position.w;

}
[/java]

(UnshadedPointSprite.frag)
[java]
uniform vec4 m_Color;

#if defined(HAS_GLOWMAP) || defined(HAS_COLORMAP) || (defined(HAS_LIGHTMAP) && !defined(SEPARATE_TEXCOORD))
#define NEED_TEXCOORD1
#endif

#ifdef HAS_COLORMAP
uniform sampler2D m_ColorMap;
#endif

#ifdef NEED_TEXCOORD1
varying vec2 texCoord1;
#endif

#ifdef HAS_LIGHTMAP
uniform sampler2D m_LightMap;
#ifdef SEPARATE_TEXCOORD
varying vec2 texCoord2;
#endif
#endif

#ifdef HAS_VERTEXCOLOR
varying vec4 vertColor;
#endif

#ifdef HAS_POINTSPRITECOORDINATES
varying vec2 pointTexSize;
varying vec2 pointTexCoord;
#endif

void main(){
vec4 color = vec4(1.0);

#ifdef HAS_VERTEXCOLOR
    color *= vertColor;
#endif

#ifdef HAS_COLORMAP
#ifdef HAS_POINTSPRITECOORDINATES
color = texture2D(m_ColorMap,vec2(pointTexCoord.x+pointTexSize.xgl_PointCoord.x,pointTexCoord.y+pointTexSize.y*(1-gl_PointCoord.y)));
#else
color *= texture2D(m_ColorMap,vec2(gl_PointCoord.x,1-gl_PointCoord.y));
#endif

#endif

#ifdef HAS_COLOR
    color *= m_Color;
#endif

#ifdef HAS_LIGHTMAP
    #ifdef SEPARATE_TEXCOORD
        color.rgb *= texture2D(m_LightMap, texCoord2).rgb;
    #else
        color.rgb *= texture2D(m_LightMap, texCoord1).rgb;
    #endif
#endif

gl_FragColor = color;

}
[/java]

Limitations of point sprites may be a problem for you here. Different graphics cards limit the size to different values…and for some that size is very small indeed.

@zarch said:
Limitations of point sprites may be a problem for you here. Different graphics cards limit the size to different values...and for some that size is very small indeed.

Yeah, and it’s the on screen size… no matter what resolution you are running. So if a sprite can’t be bigger than 64 on-screen pixels, that’s pretty tiny on a 1920x1200 display.

Also, point sprites are always oriented to the camera… which seems like it would be a bad thing for imposters. By always oriented, I mean that if you were laying on your side then the top of the sprite is still at the top of your screen.

Different graphics cards limit the size to different values…and for some that size is very small indeed.
eah, and it’s the on screen size… no matter what resolution you are running. So if a sprite can’t be bigger than 64 on-screen pixels, that’s pretty tiny on a 1920×1200 display.

Good to know… that’s really a problem. My graphics card doesen’t seem to have any limit there though. Already tried size of 500000. Well culling issues do occour then…

Perhaps it’s better to avoid pointsprites :confused:

@naas said:
Different graphics cards limit the size to different values…and for some that size is very small indeed.
eah, and it’s the on screen size… no matter what resolution you are running. So if a sprite can’t be bigger than 64 on-screen pixels, that’s pretty tiny on a 1920×1200 display.

Good to know… that’s really a problem. My graphics card doesen’t seem to have any limit there though. Already tried size of 500000. Well culling issues do occour then…

Again, I will stress that it’s the on-screen size and not some texture size limit. There is no way that you could have tested a size of 500000 since I’ve never seen a monitor with that many pixels on it.

Take a texture of any size. 500000 texels if you like. Now, imagine that being drawn as a 64x64 pixel sprite on screen. So if you had a 24" monitor at 1920x1200 that’s less than an inch on-screen space. That’s the biggest your sprite can be on many many graphics cards. No matter how big the texture is.

It is a pretty serious problem. I have to rewrite a ton of shader particle effects to deal with this issue. And in my case, the fact that the sprites are always oriented to screen is fine. I would think in your case that’s a deal-breaker.

@pspeed said:

Again, I will stress that it’s the on-screen size and not some texture size limit. There is no way that you could have tested a size of 500000 since I’ve never seen a monitor with that many pixels on it.

Take a texture of any size. 500000 texels if you like. Now, imagine that being drawn as a 64x64 pixel sprite on screen. So if you had a 24" monitor at 1920x1200 that’s less than an inch on-screen space. That’s the biggest your sprite can be on many many graphics cards. No matter how big the texture is.

It is a pretty serious problem. I have to rewrite a ton of shader particle effects to deal with this issue. And in my case, the fact that the sprites are always oriented to screen is fine. I would think in your case that’s a deal-breaker.

Yeah true its not possible to draw 500000² pixels on a normal screen :smiley:
What i did was just to set the gl_PointSize in the vertex shader at that big value and the resulting pointsprite filled the whole screen while the texture was scaled accordingly. So i just wanted to mention that… It’s a Radeon 4330 i ran it on the way.

What type of objects are you trying to create impostors for?

Typically, they are 2-sided quads… set up in a star shape if looking down on them. Depending on the type of object your trying to emulate, you can potentially get away 3 2-sided quads, each rotated 33 degrees along the Y axis… though I’ve heard some people use far more than this. I hear you have to render the object from the 3 perspectives… but that once again depends on what you’re emulating.

In my case, I would probably use 6 2-side , and pre-render the single object as the scene loaded… as I’m using the same objects over and over and over again.

Anyways… I’ll definitely be watching progress here, as I have to tackle this issue soon as well.

@t0neg0d:

Yes there are so much possibilities to arrange the impostors and i have no idea how they perform for which type of object. At the moment i want to use it to fake a volumetric galaxy ( would be very similar to volumetric cloud shading if it works). I had 2 possible arrangements in my mind and for now i try to implement “B1”. I think “A” would be more suitable if you have a solid object that is present multiple times with different angles to the camera. I guess my “A” is also what you mentioned by “6 2-side”.
I don’t understand what “3 2-sided quads, each rotated 33 degrees along the Y axis” means though… like “C”?

Imagine you are looking down on the imposter from above:

1 plane: |
2 planes: +
3 planes:
|/
/|
4 planes:

|/
/

So 2 planes have 90 degrees between them, 3 planes 66 degrees, 4 planes 45 degrees, etc.