Skip to content

Commit

Permalink
Render pipeline (#435)
Browse files Browse the repository at this point in the history
* rewrite git history for cleaning useless textures and models. Basically, this commit finishes the integration of render-pipeline

* remove old pipeline code

* update tools

* add procedual 3d

* drive in 3d scenarios!

* cool terrain

* default flat terrain

* update default daytime

* make video

* format
  • Loading branch information
QuanyiLi committed Apr 30, 2023
1 parent ef4b989 commit 7c9cf06
Show file tree
Hide file tree
Showing 921 changed files with 160,417 additions and 315 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@
**/test_10maps.pickle
/metadrive/tests/test_functionality/test_dump_single.pkl
**/test_export/
**/test_export_record_scenario/
**/test_export_record_scenario/
/metadrive/assets/textures/heightfield.png
189 changes: 189 additions & 0 deletions metadrive/assets/effect/terrain_effect.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
# Author: @QuanyiLi. This terrain effect can texture road surface/road line. It is modified from:
# terrain-effect by @tobspr: https://github.com/tobspr/RenderPipeline
# Panda3D shader-terrain example: https://github.com/panda3d/panda3d/tree/master/samples/shader-terrain
# texture splatting @wezu: https://gist.github.com/wezu/3892a0a61ae9ddfe57c0aa3f22825ac0#file-terrain_tex_f-glsl-L49

vertex:
inout: |
uniform struct {
sampler2D data_texture;
sampler2D heightfield;
int view_index;
int terrain_size;
int chunk_size;
} ShaderTerrainMesh;
out vec2 terrain_uv;
transform: |
// 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;
// Lower the terrain on the borders - this ensures the shadow map is generated
// correctly.
if ( min(terrain_uv.x, terrain_uv.y) < 8.0 / ShaderTerrainMesh.terrain_size ||
max(terrain_uv.x, terrain_uv.y) > 1 - 9.0 / ShaderTerrainMesh.terrain_size) {
chunk_position.z = 0;
}
vOutput.position = (p3d_ModelMatrix * vec4(chunk_position, 1)).xyz;
fragment:
defines: |
#define DONT_FETCH_DEFAULT_TEXTURES 0
#define DONT_SET_MATERIAL_PROPERTIES 1
inout: |
layout(location=4) in vec2 terrain_uv;
in vec3 vtx_pos;
out vec4 color;
layout(location=5) uniform struct {
sampler2D data_texture;
sampler2D heightfield;
int view_index;
int terrain_size;
int chunk_size;
} ShaderTerrainMesh;
// texture for build road
uniform sampler2D yellow_tex;
uniform sampler2D white_tex;
uniform sampler2D road_tex;
uniform sampler2D road_normal;
uniform sampler2D road_rough;
uniform sampler2D grass_tex;
uniform sampler2D grass_normal;
uniform sampler2D grass_rough;
uniform sampler2D rock_tex;
uniform sampler2D rock_normal;
uniform sampler2D rock_rough;
uniform sampler2D attribute_tex;
material: |
float terrain_ratio = 4.0;
float road_tex_ratio = 32.0 * terrain_ratio;
float grass_tex_ratio = 128.0 * terrain_ratio;
vec4 attri = texture(attribute_tex, terrain_uv*terrain_ratio+0.5);
const float terrain_height = 120.0;
vec3 pixel_size = vec3(1.0, -1.0, 0) / textureSize(ShaderTerrainMesh.heightfield, 0).xxx;
float h_u0 = texture(ShaderTerrainMesh.heightfield, terrain_uv + pixel_size.yz).x * terrain_height;
float h_u1 = texture(ShaderTerrainMesh.heightfield, terrain_uv + pixel_size.xz).x * terrain_height;
float h_v0 = texture(ShaderTerrainMesh.heightfield, terrain_uv + pixel_size.zy).x * terrain_height;
float h_v1 = texture(ShaderTerrainMesh.heightfield, terrain_uv + pixel_size.zx).x * terrain_height;
vec3 tangent = normalize(vec3(1, 0, h_u1 - h_u0));
vec3 binormal = normalize(vec3(0, 1, h_v1 - h_v0));
vec3 normal = normalize(cross(tangent, binormal));
// normal.x *= -1;
mat3 tbn = mat3(tangent, binormal, normal);
if ((attri.g > 0 || attri.a > 0 || attri.b > 0) && terrain_uv.x>0.375 && terrain_uv.y >0.375 && terrain_uv.x<0.625 && terrain_uv.y<0.625){
// texture splatting, mixing ratio can be determined via rgba, no grass here
vec3 diffuse = texture(road_tex, terrain_uv * road_tex_ratio).rgb * attri.g;
diffuse += texture(yellow_tex, terrain_uv * road_tex_ratio).rgb * attri.b;
diffuse += texture(white_tex, terrain_uv * road_tex_ratio).rgb * attri.a;
m.shading_model = SHADING_MODEL_DEFAULT;
m.specular_ior = 1.51;
m.metallic = 0.0;
m.roughness = texture(road_rough, terrain_uv * road_tex_ratio).r;
m.shading_model_param0 = 0.0;
m.normal = normalize(texture(road_normal, terrain_uv * road_tex_ratio).rgb*2.0-1.0);
m.basecolor = diffuse.xyz;
m.normal = normalize(tbn * m.normal);
}
else{
// Material splatting
float height = (h_u0 + h_u1 + h_v0 + h_v1) / (4.0 * terrain_height); // xxx
float slope = 1.0 - normal.z;
float grass = 0.0;
float rock = 0.0;
float snow = 0.0;
{ // Snow
snow = saturate(4.0 * (height-0.49));
snow *= saturate(pow(saturate(1.0 - slope), 2.0)) * 12.0;
//snow -= 0.6;
//snow *= 0.5;
snow = saturate(snow);
snow = pow(snow, 2.0);
}
{ // Rock
rock = saturate((pow(slope, 1.2) * 12.0 - 0.02) * 4.5);
}
{ // Grass
grass = 1.0 - saturate(rock + snow);
}
// Material definitions
MaterialShaderOutput grass_mat = make_default_material_output();
grass_mat.basecolor = texture(grass_tex, terrain_uv * grass_tex_ratio).rgb;
grass_mat.roughness = texture(grass_rough, terrain_uv * grass_tex_ratio).r;
grass_mat.normal = texture(grass_normal, terrain_uv * grass_tex_ratio).rgb*2.0-1.0;
grass_mat.specular_ior = 0.;
MaterialShaderOutput rock_mat = make_default_material_output();
rock_mat.basecolor = texture(rock_tex, terrain_uv * grass_tex_ratio).rgb;
rock_mat.roughness = texture(rock_rough, terrain_uv * grass_tex_ratio).r;
rock_mat.normal = texture(rock_normal, terrain_uv * grass_tex_ratio).rgb*2.0-1.0;
rock_mat.specular_ior = 1.7;
MaterialShaderOutput snow_mat = make_default_material_output();
snow_mat.basecolor = vec3(0.6, 0.6, 0.9);
snow_mat.roughness = 0.5;
snow_mat.specular_ior = 1.7;
m.basecolor = vec3(0);
m.shading_model = SHADING_MODEL_DEFAULT;
m.specular_ior = 0.0;
m.metallic = 0.0;
m.roughness = 0.0;
m.shading_model_param0 = 0.0;
m.normal = vec3(0);
merge_material_output(m, grass_mat, grass);
merge_material_output(m, rock_mat, rock);
merge_material_output(m, snow_mat, snow);
m.normal = normalize(tbn * m.normal);
}
12 changes: 12 additions & 0 deletions metadrive/assets/procedural3d/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Author: Epihaius
# Date: 2019-06-21
#
# This package contains classes to procedurally create 3D primitives such as
# boxes, spheres and cylinders.
# see: https://github.com/Epihaius/procedural_panda3d_model_primitives for more details

from .box import BoxMaker
from .cylinder import CylinderMaker
from .cone import ConeMaker
from .sphere import SphereMaker
from .torus import TorusMaker
183 changes: 183 additions & 0 deletions metadrive/assets/procedural3d/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
# Author: Epihaius
# Date: 2019-06-21
#
# This module contains a couple of imports and a base class needed for the
# creation of all 3D primitives.

from panda3d.core import *
from math import pi, sin, cos, acos, atan2, sqrt
import array


class ModelMaker:
@property
def segments(self):
return self._segments

@segments.setter
def segments(self, segments):
self._segments = segments

@property
def inverted(self):
return self._inverted

@inverted.setter
def inverted(self, inverted):
self._inverted = inverted

@property
def vertex_color(self):
return self._vertex_color

@vertex_color.setter
def vertex_color(self, vertex_color):
self._vertex_color = vertex_color

@property
def has_uvs(self):
return self._has_uvs

@has_uvs.setter
def has_uvs(self, has_uvs):
self._has_uvs = has_uvs

@property
def tex_units(self):
return self._tex_units

@tex_units.setter
def tex_units(self, tex_units):
self._tex_units = tex_units

@property
def tex_offset(self):
return self._tex_offset

@tex_offset.setter
def tex_offset(self, tex_offset):
self._tex_offset = tex_offset

@property
def tex_rotation(self):
return self._tex_rotation

@tex_rotation.setter
def tex_rotation(self, tex_rotation):
self._tex_rotation = tex_rotation

@property
def tex_scale(self):
return self._tex_scale

@tex_scale.setter
def tex_scale(self, tex_scale):
self._tex_scale = tex_scale

@property
def vertex_ranges(self):
"""
The ranges of vertex indices for each surface, as a dict of
(start_index, end_index) tuples (empty tuple if the surface
was not created), with end_index not included in the range.
See derived class documentation for available surfaces.
"""
return self._vert_ranges

def __init__(
self, segments, inverted, vertex_color, has_uvs, tex_units, tex_offset, tex_rotation, tex_scale, surface_ids
):
"""
This class generates model primitives with the given parameters, common to
all primitive types:
segments (dict of ints):
the number of subdivisions of each surface (listed in derived class
documentation);
default = None (use defaults for all surfaces);
inverted (bool):
whether or not the geometry should be rendered inside-out;
default is False;
vertex_color (tuple or None):
the color applied to all vertices (if not specified, the vertex format
of the created geometry will not allow any color data to be set);
default = None;
has_uvs (bool):
whether or not the model should have texture coordinates;
default is True;
tex_units (dict of float tuples):
the texture size (width, height) in object-space units for each
surface (listed in derived class documentation);
default = None;
tex_offset, tex_rotation, tex_scale (dicts of float tuples):
the 2D texture transforms in object-space for each surface
(listed in derived class documentation):
tex_offset: (u offset, v offset);
tex_rotation: angle in degrees;
tex_scale: (u scale, v scale);
default = None.
"""

self._segments = segments
self._inverted = inverted
self._vertex_color = vertex_color
self._has_uvs = has_uvs
self._tex_units = tex_units
self._tex_offset = tex_offset
self._tex_rotation = tex_rotation
self._tex_scale = tex_scale
self._surface_ids = surface_ids
self._vert_ranges = {s_id: () for s_id in surface_ids}

def reset(self):

self._segments = None
self._inverted = False
self._vertex_color = None
self._has_uvs = True
self._tex_units = None
self._tex_offset = None
self._tex_rotation = None
self._tex_scale = None
self._vert_ranges = {s_id: () for s_id in self._surface_ids}

def _make_flat_shaded(self, indices, verts):

points = [Point3(verts[i]["pos"]) for i in indices[:3]]
normal = Plane(*points).get_normal()

for i in indices:
verts[i]["normal"] = normal

def _average_normals(self, index1, index2, verts):

normal = (verts[index1]["normal"] + verts[index2]["normal"]).normalized()
verts[index1]["normal"] = normal
verts[index2]["normal"] = normal

def _get_tex_xform(self, surface_id):

tex_offset = self._tex_offset
tex_rotation = self._tex_rotation
tex_scale = self._tex_scale

has_tex_offset = tex_offset and surface_id in tex_offset
has_tex_rot = tex_rotation and surface_id in tex_rotation
has_tex_scale = tex_scale and surface_id in tex_scale
has_tex_xform = has_tex_offset or has_tex_rot or has_tex_scale

if has_tex_xform:

mat = Mat3.ident_mat()

if has_tex_scale:
mat = mat * Mat3.scale_mat(*tex_scale[surface_id])
if has_tex_rot:
mat = mat * Mat3.rotate_mat(tex_rotation[surface_id])
if has_tex_offset:
mat = mat * Mat3.translate_mat(*tex_offset[surface_id])

return mat

0 comments on commit 7c9cf06

Please sign in to comment.