i want that left map have lot of trees and right map nothing, I’m not very good at glsl
is there any code in panda3d that could help me do this or who does this?
You might want to do this on the CPU and not the GPU (e.g., with GLSL). I would probably do something like the following when loading the scene:
Define a way to sample the tree map. This would likely be a 2D uniform grid over the terrain.
For each sample point, do a lookup into the texture/image using PNMImage
Based on the result, place an instance of the object.
This could result in a lot of objects, which could have pretty noticeable performance impacts. To fix this, I would probably use some of the NodePath.flatten_*() functions. In particular, flatten_strong().
EDIT: Doing this on the GPU would likely be a lot faster to render, but it gets a lot more complicated. It would likely involve the use of a compute shader and multi-draw indirect type features (not show how/if these are exposed in Panda). This would also limit the type of graphics hardware that could run the game. So, I would only look toward the GPU if the CPU solution is still too slow.
I had a dictionary of (R,G,B) keys that corresponded to colors in a PNMImage. The value held something like {‘path’: ‘path/to/model_or_picture’, ‘is_billboard’: True }. That way you have the path, and you can switch between loading a model and making an image into a billboard (CardMaker). Of course, you could just check the extension.
Then everything was under a dummy node, so I dummyNP.findAllMatches("**/*" + self.modelEnding + self.instanceEnding) and removed all models outside a given radius.
If you use GeoMipTerrain, don’t forget to play with setBlockSIze and setNearFar for performance.
I’ve written some sample code to show how to place many copies of the same object on a heightmap terrain, here I’m using ~21.5k (hardware) instances of grass on a ShaderMeshTerrain, but the principle is the same for trees, rocks or whatever you need:
(you may want to clone/download the whole repo to get the models and textures if you want to run it)
but all 3d grass object appears or disappears at the same time, I think it’s because panda3d loads only 1 object
do I have to go through the glsl files for the lod or is it possible to use LODNode ?
If you want both LOD and hardware instancing you will need to divide the objects into smaller chunks and decide based on the distance to camera what model (lod level) use for all the objects in that chunk.
Arranging the chunks in a octree/quadtree may also be a good idea.
I used a grass model made from 8 triangles so lod is kind of pointless in thiscase, overdraw is probably the bottleneck here not vertex count.
There are a few options if you just want to disable far away objects:
You can discard fragments in the fragment shader based on distance (but I’d suggest doing a benchmark to see of that has any performance benefits for your scene/hardware).
You can make degenerated triangles (set all vertex coordinates to 0.0) in the vertex shader - just make sure you do it for all verts of a triangle. Using gl_ClipDistance could work better for this, but this feature isn’t supported at the moment.
You can finally use a geometry shader to cull primitives - but you’d have to use a geometry shader, and the first rule of using geometry shaders is ‘don’t use geometry shaders’.
grass_v.glsl is the vertex shader, grass_f.glsl is the fragment shader.
You can’t print from glsl to the console, the shader code is run on the gpu, not the cpu (I suppose if you try very, very hard you could).
The best way I can think of of getting the camera position:
-set a shader input with the camera node: self.grass.set_shader_input('camera', self.camera)
now in the shaders you can use uniform vec3 wspos_camera; to get the world pos of the camera node (maybe there’s also one for view space? idk, I’ll go ask on discord…)
use the p3d_ModelMatrix to transform the vertex into world space
use the distance() function to get the distance from camera to the vertex.
use the discard; statement to discard fragments.
I’m not sure where’s the best place to add this code, you can calculate the world space position in the vertex shader and pass it to the fragment shader or calculate the distance on the vertex and pass just a 0.0 or 1.0 float to the fragment shader telling it to either draw the fragments or discard.
If you’ll have problems with this, let me know, I’ll add the code to do that in the sample I linked earlier.
EDIT:
Actually, some of what I’ve written above is stupid. Yeah, it should work, but the easy way to get the distance to the camera on the vertex shader is just: float distance_to_camera= -vec4(p3d_Vertex * p3d_ModelViewMatrix).z;
is there a way to debug glsl code easily ?
i create this variable float distance_to_camera= -vec4(p3d_Vertex * p3d_ModelViewMatrix).z;
but i can not print this variable i don’t know if this code works (if my code is bad I have glsl error in console)
i update the code with your solution with distance_to_camera, maybe i need edit grass_v.glsl :
//GLSL #version 150
There are some errors in the shader and you need to change both the vertex and fragment shaders. Not writing to gl_Position will not make the polygons disappear, it will probably fail to compile.
Try this (not tested, I’m currently on mobile):
grass_v.glsl:
//GLSL
#version 150
in vec4 p3d_Vertex;
// p3d_ModelViewMatrix is a uniform matrix, not a vertex attribute
// in vec4 p3d_ModelViewMatrix;
uniform mat4 p3d_ModelViewMatrix;
in vec2 p3d_MultiTexCoord0;
uniform samplerBuffer grass_buf_tex;
uniform mat4 p3d_ModelViewProjectionMatrix;
uniform float osg_FrameTime;
out vec2 UV;
// we can not discard a vertex, so we will pass the distance to camera
// to the fragment shader, because it can discard fragments
out float distance_to_camera;
void main()
{
vec3 pos_offset= texelFetch(grass_buf_tex, gl_InstanceID).xyz;
vec4 vertex=p3d_Vertex;
distance_to_camera= -vec4(p3d_Vertex * p3d_ModelViewMatrix).z;
//the verex shader *must* write to gl_Position
//without that the position of the vertex is undefined and probably should raise an error
//if (distance_to_camera < 5) {
float anim_co=vertex.z*0.2;
float animation =sin(0.7*osg_FrameTime+float(gl_InstanceID))*sin(1.7*osg_FrameTime+float(gl_InstanceID))*anim_co;
vertex.xy += animation;
vertex.xyz+=pos_offset;
gl_Position = p3d_ModelViewProjectionMatrix *vertex;
UV = p3d_MultiTexCoord0;
//}
}
grass_f.glsl:
#version 150
in vec2 UV;
//distance to camera, value passed from the vert shader
in float distance_to_camera;
out vec4 final_color;
uniform sampler2D p3d_Texture0;
void main()
{
//the value here probably needs to be tweaked
if (distance_to_camera > 5.0)
{
//do not write the fragment using the discard statement.
discard;
}
vec4 color = texture(p3d_Texture0,UV);
final_color=color;
}
I’ve made a few errors myself. The updated, tested shaders are over on github. I just want to note that on my PC, with this scene, these distances, this amount of grass, these models, discarding the grass based on distance has no significant performance effect.