Browse Source

Add ShaderTerrainMesh sample

tobspr 9 years ago
parent
commit
84f5baaf2c

+ 2 - 1
panda/src/grutil/shaderTerrainMesh.cxx

@@ -629,8 +629,9 @@ bool ShaderTerrainMesh::do_check_lod_matches(Chunk* chunk, TraversalData* data)
         chunk->edges.get_cell(x + 2 * y)
       );
       LVector4 projected = data->mvp_mat.xform(LVector4(edge_pos, 1.0));
-      if (projected.get_w() == 0.0)
+      if (projected.get_w() == 0.0) {
         projected.set(0.0, 0.0, -1.0, 1.0f);
+      }
       projected *= 1.0 / projected.get_w();
       projected_points[x + 2 * y].set(
         projected.get_x() * data->screen_size.get_x(),

BIN
samples/shader-terrain/heightfield.png


+ 80 - 0
samples/shader-terrain/main.py

@@ -0,0 +1,80 @@
+#!/usr/bin/env python
+
+# Author: tobspr
+#
+# Last Updated: 2016-02-13
+#
+# This tutorial provides an example of using the ShaderTerrainMesh class
+
+import os, sys, math, random
+
+from direct.showbase.ShowBase import ShowBase
+from panda3d.core import ShaderTerrainMesh, Shader, load_prc_file_data
+from panda3d.core import SamplerState
+
+class ShaderTerrainDemo(ShowBase):
+    def __init__(self):
+
+        # Load some configuration variables, its important for this to happen
+        # before the ShowBase is initialized
+        load_prc_file_data("", """
+            textures-power-2 none
+            window-title Panda3D Shader Terrain Demo
+        """)
+
+        # Initialize the showbase
+        ShowBase.__init__(self)
+
+        # Increase camera FOV aswell as the far plane
+        self.camLens.set_fov(90)
+        self.camLens.set_near_far(0.1, 50000)
+
+        # Construct the terrain
+        self.terrain_node = ShaderTerrainMesh()
+
+        # Set a heightfield, the heightfield should be a 16-bit png and
+        # have a quadratic size of a power of two.
+        self.terrain_node.heightfield_filename = "heightfield.png"
+
+        # Set the target triangle width. For a value of 10.0 for example,
+        # the terrain will attempt to make every triangle 10 pixels wide on screen.
+        self.terrain_node.target_triangle_width = 10.0
+
+        # Generate the terrain
+        self.terrain_node.generate()
+
+        # Attach the terrain to the main scene and set its scale
+        self.terrain = self.render.attach_new_node(self.terrain_node)
+        self.terrain.set_scale(1024, 1024, 100)
+        self.terrain.set_pos(-512, -512, -70.0)
+
+        # Set a shader on the terrain. The ShaderTerrainMesh only works with
+        # an applied shader. You can use the shaders used here in your own shaders
+        terrain_shader = Shader.load(Shader.SL_GLSL, "terrain.vert.glsl", "terrain.frag.glsl")
+        self.terrain.set_shader(terrain_shader)
+        self.terrain.set_shader_input("camera", self.camera)
+
+        # Set some texture on the terrain
+        grass_tex = self.loader.loadTexture("textures/grass.png")
+        grass_tex.set_minfilter(SamplerState.FT_linear_mipmap_linear)
+        grass_tex.set_anisotropic_degree(16)
+        self.terrain.set_texture(grass_tex)
+
+        # Load some skybox - you can safely ignore this code
+        skybox = self.loader.loadModel("models/skybox.bam")
+        skybox.reparent_to(self.render)
+        skybox.set_scale(20000)
+
+        skybox_texture = self.loader.loadTexture("textures/skybox.jpg")
+        skybox_texture.set_minfilter(SamplerState.FT_linear)
+        skybox_texture.set_magfilter(SamplerState.FT_linear)
+        skybox_texture.set_wrap_u(SamplerState.WM_repeat)
+        skybox_texture.set_wrap_v(SamplerState.WM_mirror)
+        skybox_texture.set_anisotropic_degree(16)
+        skybox.set_texture(skybox_texture)
+
+        skybox_shader = Shader.load(Shader.SL_GLSL, "skybox.vert.glsl", "skybox.frag.glsl")
+        skybox.set_shader(skybox_shader)
+
+demo = ShaderTerrainDemo()
+demo.run()

BIN
samples/shader-terrain/models/skybox.bam


+ 21 - 0
samples/shader-terrain/skybox.frag.glsl

@@ -0,0 +1,21 @@
+#version 150
+
+in vec3 skybox_pos;
+out vec4 color;
+
+uniform sampler2D p3d_Texture0;
+
+void main() {
+
+  vec3 view_dir = normalize(skybox_pos);
+  vec2 skybox_uv;
+
+  // Convert spherical coordinates
+  const float pi = 3.14159265359;
+  skybox_uv.x = (atan(view_dir.y, view_dir.x) + (0.5 * pi)) / (2 * pi);
+  skybox_uv.y = clamp(view_dir.z * 0.72 + 0.35, 0.0, 1.0);
+
+  vec3 skybox_color = textureLod(p3d_Texture0, skybox_uv, 0).xyz;
+
+  color = vec4(skybox_color, 1);
+}

+ 13 - 0
samples/shader-terrain/skybox.vert.glsl

@@ -0,0 +1,13 @@
+#version 150
+
+// This is just a simple vertex shader transforming the skybox
+
+in vec4 p3d_Vertex;
+uniform mat4 p3d_ModelViewProjectionMatrix;
+
+out vec3 skybox_pos;
+
+void main() {
+  skybox_pos = p3d_Vertex.xyz;
+  gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex;
+}

+ 56 - 0
samples/shader-terrain/terrain.frag.glsl

@@ -0,0 +1,56 @@
+#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;
+
+uniform struct {
+  sampler2D data_texture;
+  sampler2D heightfield;
+  int view_index;
+  int terrain_size;
+  int chunk_size;
+} ShaderTerrainMesh;
+
+uniform sampler2D p3d_Texture0;
+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(p3d_Texture0, terrain_uv * 16.0).xyz;
+  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);
+}

+ 56 - 0
samples/shader-terrain/terrain.vert.glsl

@@ -0,0 +1,56 @@
+#version 150
+
+// This is the default terrain vertex shader. Most of the time you can just copy
+// this and reuse it, and just modify the fragment shader.
+
+in vec4 p3d_Vertex;
+uniform mat4 p3d_ModelViewProjectionMatrix;
+uniform mat4 p3d_ModelMatrix;
+
+uniform struct {
+  sampler2D data_texture;
+  sampler2D heightfield;
+  int view_index;
+  int terrain_size;
+  int chunk_size;
+} ShaderTerrainMesh;
+
+out vec2 terrain_uv;
+out vec3 vtx_pos;
+
+void main() {
+
+  // Terrain data has the layout:
+  // x: x-pos, y: y-pos, z: size, w: clod
+  vec4 terrain_data = texelFetch(ShaderTerrainMesh.data_texture,
+    ivec2(gl_InstanceID, ShaderTerrainMesh.view_index), 0);
+
+  // Get initial chunk position in the (0, 0, 0), (1, 1, 0) range
+  vec3 chunk_position = p3d_Vertex.xyz;
+
+  // CLOD implementation
+  float clod_factor = smoothstep(0, 1, terrain_data.w);
+  chunk_position.xy -= clod_factor * fract(chunk_position.xy * ShaderTerrainMesh.chunk_size / 2.0)
+                          * 2.0 / ShaderTerrainMesh.chunk_size;
+
+  // Scale the chunk
+  chunk_position *= terrain_data.z * float(ShaderTerrainMesh.chunk_size)
+                    / float(ShaderTerrainMesh.terrain_size);
+  chunk_position.z *= ShaderTerrainMesh.chunk_size;
+
+  // Offset the chunk, it is important that this happens after the scale
+  chunk_position.xy += terrain_data.xy / float(ShaderTerrainMesh.terrain_size);
+
+  // Compute the terrain UV coordinates
+  terrain_uv = chunk_position.xy;
+
+  // Sample the heightfield and offset the terrain - we do not need to multiply
+  // the height with anything since the terrain transform is included in the
+  // model view projection matrix.
+  chunk_position.z += texture(ShaderTerrainMesh.heightfield, terrain_uv).x;
+  gl_Position = p3d_ModelViewProjectionMatrix * vec4(chunk_position, 1);
+
+  // Output the vertex world space position - in this case we use this to render
+  // the fog.
+  vtx_pos = (p3d_ModelMatrix * vec4(chunk_position, 1)).xyz;
+}

+ 5 - 0
samples/shader-terrain/textures/LICENSE.txt

@@ -0,0 +1,5 @@
+Grass texture from (cc by 3.0)
+http://opengameart.org/content/grass-texture
+
+Skybox by rdb
+http://rdb.name/PANO_20140818_112419.jpg

BIN
samples/shader-terrain/textures/grass.png


BIN
samples/shader-terrain/textures/skybox.jpg