Glsl convert ShaderTerrainMesh plane to sphere with mercator projection

Hello, i want to convert plane surface ShaderTerrainMesh to sphere with use mercator projection
in python i use math library for convert all 2d point for my map like this :

import math
def y2lat(a):
  return 180.0/math.pi*(2.0*math.atan(math.exp(a*math.pi/180.0))-math.pi/2.0)
def lat2y(a):
  return 180.0/math.pi*math.log(math.tan(math.pi/4.0+a*(math.pi/180.0)/2.0))


but for use ShaderTerrainMesh, i think that i must modify glsl file.
My question is, how modify coordinate of point in glsl code for apply mercator projection

my glsl file :

#version 150

// This is the terrain fragment shader. There is a lot of code in here
// which is not necessary to render the terrain, but included for convenience -
// Like generating normals from the heightmap or a simple fog effect.

// Most of the time you want to adjust this shader to get your terrain the look
// you want. The vertex shader most likely will stay the same.


in vec2 terrain_uv;
in vec3 vtx_pos;
out vec4 color;
int count = 0;

uniform struct {
  sampler2D data_texture;
  sampler2D heightfield;
  int view_index;
  int terrain_size;
  int chunk_size;
} ShaderTerrainMesh;

uniform sampler2D detail_tex1;
uniform sampler2D detail_tex2;
uniform sampler2D detail_tex3;
uniform sampler2D detail_tex4;
uniform sampler2D attribute_tex;
uniform vec3 wspos_camera;

// Compute normal from the heightmap, this assumes the terrain is facing z-up
vec3 get_terrain_normal() {
  const float terrain_height = 50.0;
  vec3 pixel_size = vec3(1.0, -1.0, 0) / textureSize(ShaderTerrainMesh.heightfield, 0).xxx;
  float u0 = texture(ShaderTerrainMesh.heightfield, terrain_uv + pixel_size.yz).x * terrain_height;
  float u1 = texture(ShaderTerrainMesh.heightfield, terrain_uv + pixel_size.xz).x * terrain_height;
  float v0 = texture(ShaderTerrainMesh.heightfield, terrain_uv + pixel_size.zy).x * terrain_height;
  float v1 = texture(ShaderTerrainMesh.heightfield, terrain_uv + pixel_size.zx).x * terrain_height;
  vec3 tangent = normalize(vec3(1.0, 0, u1 - u0));
  vec3 binormal = normalize(vec3(0, 1.0, v1 - v0));
  return normalize(cross(tangent, binormal));

void main() {

  vec3 diffuse = texture(detail_tex1, terrain_uv * 16.0).rgb * texture(attribute_tex, terrain_uv).r;
  diffuse += texture(detail_tex2, terrain_uv * 16.0).rgb * texture(attribute_tex, terrain_uv).g;
  diffuse += texture(detail_tex3, terrain_uv * 16.0).rgb * texture(attribute_tex, terrain_uv).b;
  diffuse += texture(detail_tex4, terrain_uv * 16.0).rgb * texture(attribute_tex, terrain_uv).a;

  vec3 normal = get_terrain_normal();

  // Add some fake lighting - you usually want to use your own lighting code here
  vec3 fake_sun = normalize(vec3(0.7, 0.2, 0.6));
  vec3 shading = max(0.0, dot(normal, fake_sun)) * diffuse;
  shading += vec3(0.07, 0.07, 0.1);

  // Fake fog
  /*float dist = distance(vtx_pos, wspos_camera);
  float fog_factor = smoothstep(0, 1, dist / 1000.0);
  shading = mix(shading, vec3(0.7, 0.7, 0.8), fog_factor);*/

  color = vec4(shading, 1.0);

for get height of point i add this code in main function :
float height = texture(ShaderTerrainMesh.heightfield, terrain_uv).x;

but how get and modify x,y and z coordinate ? for apply mercator projection
thanks for advance for your help

for mercator convertion, i think use this implementation :

i think i found x and y position, it’s variable terrain_uv
in vec2 terrain_uv;

for get x i use this variable
and y :

but how update this value for generate mercator projection ?

You should actually be modifying the vertex shader, not the fragment shader, and altering the vertex coordinates to wrap around a sphere rather than sprawl out horizontally. However, keep in mind that ShaderTerrainMesh is designed to perform culling on a horizontal terrain, so you may struggle to get the right result. It may be better if you apply this shader to a custom spherical base mesh that has the UV coordinates already unwrapped in a mercator-like projection.

You would then need to modify the vertex shader to make the height scale the distance from the center of the planet, rather than just affect the Z coordinate.

thanks for your help
i have 2 questions :

  1. how update vertex coordinates in my glsl file ?
    i think this is this variables : in vec3 vtx_pos;

but if i modify this in main function like this :
vtx_pos = (0.0,0.0,0.0);
vtx_pos.x = 0.0;
vtx_pos.y = 0.0;
vtx_pos.z = 0.0;
I have no change.
how to update these values in panda3d?

  1. I didn’t understand your proposal.
    For example i create sphere in opengl in my main function ands i update UV coordinates of my shadder terrain to the sphere ?
    with this technical i have same question, how update/modify uv vectors ?
    i think this is this variable
    in vec2 terrain_uv;
    but i can not edit terrain_uv.x or terrain_uv.y i have this error :
    error C7565: assignment to varying

maybe function have getter and setter ?
try this :
for example

thanks for your help
I try your solution but i have same error.
may be engine have not getter/setter

You cannot write to an input variable in the fragment shader. This is a GLSL restriction, not a Panda3D restriction. It’s not clear to me what you plan on achieving by modifying the vertex position in the fragment shader, since the fragment shader cannot change the vertex position.

Okay, but in this case how can I turn the shape of the square map into a sphere map? What should I do? if I don’t have to modify a variable.

I don’t know how to do it.

I had a similar problem a long time ago.
the solution I found is to use 4 maps to make a cube of it

i have this :

it is not a sphere and it does not look like a planet but it works while waiting for panda3d to implement this feature

thanks jipete for your help.
I thought of that solution if I couldn’t.
I don’t know the limits of pandas3d in this field, because even if I have my land, I don’t know if pandas3d will be able to support a spherical shape
the cube seems an interesting track if panda3d can’t make a sphere.

currently I’m trying to hack things, but I don’t see how to modify the rectangular shape of the map, it’s my blocking point

it seems impossible to do it from glsl

It is theoretically possible to do it from GLSL, but you are making things needlessly complicated. For one, ShaderTerrainMesh is generating a rectangular base mesh, and performing culling on it, so that will not have the desired effect.

It becomes much easier if you just make a densely tessellated sphere with the UV coordinates chosen to match your desired projection. (This can be easily done with the UV Sphere tool in Blender.) Then you can easily scale the vertices in the shader to match the desired terrain height that is sampled at the UVs.

rdb, i don’t undestand your solution.
I create a sphere in blender and export my sphere to egg format and then load it in panda3d ?
If yes, I don’t understand the rest of your solution.

Can you give me an example of how to modify the UV coordinates of a ShaderTerrainMesh ?
I must change uv in python code or glsl code ?

for generate map i have this code :
terrain_node = ShaderTerrainMesh()
terrain_node.heightfield_filename = “heightfield.png”
terrain_node.target_triangle_width = 10.0

what is UV coordinates in this code ?
i not have terrain_node.uvcoordinates for example in api :

thanks for advance for your help

i think uv is in your glsl files :
in vec2 terrain_uv;

try to change in your python code like this :
shadder.terrain_uv =(0,0)
for change variable of glsl

maybe shadder is your opengl object

thanks for your help jipete, but glsl is not python object, i can not acces ti this with shadder.terrain_uv.

python make error, attribut not exist, i don’t know how modify uv variables.

I understood the theory

but I don’t know how to apply it to the map

this is the mathematical function in wikipedia,
how access to dx and dy in panda3d ? and how update u and v variables ?

shadderterrain not support uv coordiante.
actually panda3d support only 2d texture like this

for support this, may be you must change important code in engine

i think i found best easy solution, you can convert your carre map to triangle map and generate with lot of triangle a geodesic domes

I don’t know the mathematical formula to place the triangle in a 3D space though.
this will have to be done manually at random to give the x,y and z

I am not entirely sure what @sazearte is saying, but ShaderTerrainMesh does actually write UV coordinates, and Panda3D most certainly is not limited to 2D textures.

It might help if you explained what exactly you are trying to do with the UV coordinates. From what I can tell, your problem is actually that you want to transform the XYZ vertex coordinates, not the UVs.

As stated earlier, ShaderTerrainMesh is not really designed to be transformed eg. to a planet shape (since it performs its own culling), but you can just use a sphere as the base mesh and use a similar shader to move vertices away from the origin depending on the terrain value.