diff --git a/examples/occlusion/mask.py b/examples/occlusion/mask.py index 63df25abe6..b8de004a4a 100644 --- a/examples/occlusion/mask.py +++ b/examples/occlusion/mask.py @@ -527,14 +527,30 @@ def __init__(self, inner_agent, mode, observation_radius=40) -> None: @lru_cache(1) def _get_perlin( - self, width, height, smooth_iterations, seed, table_dim, shift, amplitude=5, granularity=0.02 + self, + width, + height, + smooth_iterations, + seed, + table_dim, + shift, + amplitude=5, + granularity=0.02, ): # from smarts.sstudio.graphics.perlin_bytemap import generate_perlin # return generate_perlin(width, height, smooth_iterations, seed, table_dim, shift) from smarts.sstudio.graphics.perlin_bytemap import generate_simplex - return generate_simplex(width, height, seed, shift, octaves=2, amplitude=amplitude, granularity=granularity) + return generate_simplex( + width, + height, + seed, + shift, + octaves=2, + amplitude=amplitude, + granularity=granularity, + ) def _rotate_image(self, heightfield: HeightField, heading: float): image = Image.fromarray(heightfield.data, "L") @@ -546,7 +562,6 @@ def _rotate_image(self, heightfield: HeightField, heading: float): ) def act(self, obs: Optional[Observation], **configs): - img_width, img_height = ( obs.drivable_area_grid_map.metadata.width, obs.drivable_area_grid_map.metadata.height, @@ -579,7 +594,9 @@ def act(self, obs: Optional[Observation], **configs): vehicle_hf = HeightField.from_rgb(obs.occupancy_grid_map.data) height_scaling = 5 - drivable_hf = HeightField(obs.drivable_area_grid_map.data, (img_width, img_height)) + drivable_hf = HeightField( + obs.drivable_area_grid_map.data, (img_width, img_height) + ) edge_hf = generate_edge_from_heightfield( drivable_hf, far_kernel(), @@ -612,7 +629,9 @@ def act(self, obs: Optional[Observation], **configs): amplitude=int(2 * height_scaling), granularity=0.5, ) - offroad_hf = self._rotate_image(offroad_perlin, -ego_heading).scale_by(drivable_hf.inverted()) + offroad_hf = self._rotate_image(offroad_perlin, -ego_heading).scale_by( + drivable_hf.inverted() + ) hf = edge_hf.add(perlin_hf).max(vehicle_hf).max(offroad_hf) los = hf.to_line_of_sight( @@ -860,7 +879,6 @@ def consume(video_source_pattern="A1_%d.jpg", video_name="sd_obs.mp4"): def dummy_main(output_file): - import gymnasium as gym from smarts.env.gymnasium.hiway_env_v1 import HiWayEnvV1 diff --git a/smarts/core/glsl/simplex.comp b/smarts/core/glsl/simplex.comp new file mode 100644 index 0000000000..8a450f203e --- /dev/null +++ b/smarts/core/glsl/simplex.comp @@ -0,0 +1,58 @@ +// The MIT License +// Copyright © 2013 Inigo Quilez +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// https://www.youtube.com/c/InigoQuilez +// https://iquilezles.org + + +// Simplex Noise (http://en.wikipedia.org/wiki/Simplex_noise), a type of gradient noise +// that uses N+1 vertices for random gradient interpolation instead of 2^N as in regular +// latice based Gradient Noise. + +// All noise functions here: +// +// https://www.shadertoy.com/playlist/fXlXzf&from=0&num=12 +#version 430 + +layout (local_size_x = 16, local_size_y = 16) + +uniform writeonly image2D toNoise; + +vec2 hash( vec2 p ) // replace this by something better +{ + p = vec2( dot(p,vec2(127.1,311.7)), dot(p,vec2(269.5,183.3)) ); + return -1.0 + 2.0*fract(sin(p)*43758.5453123); +} + +float noise( in vec2 p ) +{ + const float K1 = 0.366025404; // (sqrt(3)-1)/2; + const float K2 = 0.211324865; // (3-sqrt(3))/6; + + vec2 i = floor( p + (p.x+p.y)*K1 ); + vec2 a = p - i + (i.x+i.y)*K2; + float m = step(a.y,a.x); + vec2 o = vec2(m,1.0-m); + vec2 b = a - o + K2; + vec2 c = a - 1.0 + 2.0*K2; + vec3 h = max( 0.5-vec3(dot(a,a), dot(b,b), dot(c,c) ), 0.0 ); + vec3 n = h*h*h*h*vec3( dot(a,hash(i+0.0)), dot(b,hash(i+o)), dot(c,hash(i+1.0))); + return dot( n, vec3(70.0) ); +} + +void main() { + // get the coordinates + ivec2 texelCoords = ivec2(gl_GlobalInvocationID.xy) + + uv *= 5.0; + mat2 m = mat2( 1.6, 1.2, -1.2, 1.6 ); + f = 0.5000*noise( uv ); uv = m*uv; + f += 0.2500*noise( uv ); uv = m*uv; + f += 0.1250*noise( uv ); uv = m*uv; + f += 0.0625*noise( uv ); uv = m*uv; + + f = 0.5 + 0.5*f; + + + imageStore(toNoise, texelCoords, vec4(f, f, f, 1.0)) +} \ No newline at end of file diff --git a/smarts/core/glsl/surface_facing.frag b/smarts/core/glsl/surface_facing.frag new file mode 100644 index 0000000000..1946a0d9c7 --- /dev/null +++ b/smarts/core/glsl/surface_facing.frag @@ -0,0 +1,106 @@ +// The MIT License +// Copyright © 2013 Inigo Quilez +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// https://www.youtube.com/c/InigoQuilez +// https://iquilezles.org + + +// Simplex Noise (http://en.wikipedia.org/wiki/Simplex_noise), a type of gradient noise +// that uses N+1 vertices for random gradient interpolation instead of 2^N as in regular +// latice based Gradient Noise. + +// All noise functions here: +// +// https://www.shadertoy.com/playlist/fXlXzf&from=0&num=12 +//#define SEE_DIRECTION // See the direction to the pixel +#define SHADERTOY +#define CENTER 0.5 +#define HEIGHT 1.0 +#define RECIPROCAL_BYTE 0.0039215686274509803921568627451 + +vec2 hash( vec2 p ) // replace this by something better +{ + p = vec2( dot(p,vec2(127.1,311.7)), dot(p,vec2(269.5,183.3)) ); + return -1.0 + 2.0*fract(sin(p)*43758.5453123); +} + +float noise( in vec2 p ) +{ + const float K1 = 0.366025404; // (sqrt(3)-1)/2; + const float K2 = 0.211324865; // (3-sqrt(3))/6; + + vec2 i = floor( p + (p.x+p.y)*K1 ); + vec2 a = p - i + (i.x+i.y)*K2; + float m = step(a.y,a.x); + vec2 o = vec2(m,1.0-m); + vec2 b = a - o + K2; + vec2 c = a - 1.0 + 2.0*K2; + vec3 h = max( 0.5-vec3(dot(a,a), dot(b,b), dot(c,c) ), 0.0 ); + vec3 n = h*h*h*h*vec3( dot(a,hash(i+0.0)), dot(b,hash(i+o)), dot(c,hash(i+1.0))); + return dot( n, vec3(70.0) ); +} + +float noise_with_octaves( in vec2 uv, in mat2 m ){ + float f = 0.0; + uv *= 5.0; + + f = 0.5000*noise( uv ); uv = m*uv; + f += 0.2500*noise( uv ); uv = m*uv; + f += 0.1250*noise( uv ); uv = m*uv; + f += 0.0625*noise( uv ); uv = m*uv; + return f; +} + +// ----------------------------------------------- + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + vec2 rec_res = 1.0 / iResolution.xy; + vec2 p = fragCoord.xy * rec_res; + float aspect = iResolution.x/iResolution.y; + + #ifdef SHADERTOY + vec2 uv = p*vec2(aspect,1.0) + vec2(iTime * 0.1); + #else + vec2 uv = p*vec2(aspect,1.0); + #endif + + + // Determine the minimum delta needed to determine the facing direction of the surface + float interpolate_delta = 1.0 * min(rec_res.x, rec_res.y); + vec2 direction = normalize(p - vec2(0.5)); + vec2 uv_offset_to_closer_p = direction * (interpolate_delta)*vec2(aspect,1.0); + + #ifdef SEE_DIRECTION + uv_offset_to_closer_p *= 1000.0; + fragColor = vec4(uv_offset_to_closer_p, 0, 1.0); + return; + #endif + + float f = 0.0; + float x, y, z; + mat2 m = mat2( 1.6, 1.2, -1.2, 1.6 ); + + f = noise_with_octaves(uv, m); + // inspect fragments near mouse + if( distance(fragCoord, iMouse.xy) < 30.0 ) + { + f = 0.5 + 0.5*f; + f *= 0.3 + f; + fragColor = vec4( f, f, f, 1.0 ); + } + else { + float o = noise_with_octaves(uv - uv_offset_to_closer_p, m); + vec3 c = cross( + //vec3(direction.y, -direction.x, 0), + vec3(1, 0, 0), + normalize(vec3(interpolate_delta, 0, (f - o) * RECIPROCAL_BYTE)) + ); + fragColor = vec4(c.yyy * 25.5, 1.0); + } + + + if( distance(fragCoord, iResolution.xy/2.0) < 4.0 ) { + fragColor += vec4(0.4, 0.0, 0.0, 1.0); + } +} \ No newline at end of file diff --git a/smarts/sstudio/graphics/heightfield.py b/smarts/sstudio/graphics/heightfield.py index 4e52c51e51..fd72ab3542 100644 --- a/smarts/sstudio/graphics/heightfield.py +++ b/smarts/sstudio/graphics/heightfield.py @@ -158,7 +158,7 @@ def _get_sample_averaging_function( return self._direct_4_point_coordinate_sample - def _sample_line( + def _data_sample_line( self, change_normalized, resolution, @@ -167,9 +167,9 @@ def _sample_line( viewer_coordinate, factor, ): - inverse_resolution = 1 / resolution - dist = int(magnitude * inverse_resolution) - for i in range(1, dist): + """Generates samples on the line between `viewer_coordinate`(excluded) and the end point `viewer_coordinate*magnitude*change_normalized`(excluded)""" + dist = int(magnitude * np.reciprocal(resolution)) + for i in range(1, dist - 1): intermediary_coordinate = change_normalized * i + viewer_coordinate yield sample_function(intermediary_coordinate), i * factor @@ -200,11 +200,13 @@ def data_line_of_sight( factor = resolution / magnitude uv_slope_normalized = np.multiply(change, factor) + # MTA TODO: reverse iteration + # Cull opposite facing surfaces (on target surface) to short circuit ray marching return line_of_sight_test( viewer_height, target_height, magnitude, - self._sample_line( + self._data_sample_line( uv_slope_normalized, resolution, magnitude, diff --git a/smarts/sstudio/graphics/perlin_bytemap.py b/smarts/sstudio/graphics/perlin_bytemap.py index 2f3a8133eb..e43a0e77ec 100644 --- a/smarts/sstudio/graphics/perlin_bytemap.py +++ b/smarts/sstudio/graphics/perlin_bytemap.py @@ -18,6 +18,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. import argparse +import importlib.resources as pkg_resources import os from functools import lru_cache from typing import Tuple @@ -126,9 +127,52 @@ def generate_perlin( return hf +def generate_simplex_p3d_gpu( + width: int, + height: int, + seed, + shift: Tuple[float, float], + octaves: float = 2, + granularity=0.02, + amplitude=4, + transformation_matrix: np.ndarray = np.identity(4), +): + assert height % 16 == 0 + assert width % 16 == 0 + + from panda3d.core import ComputeNode, Shader, ShaderAttrib, Texture + + from smarts.core import glsl + from smarts.p3d.renderer import DEBUG_MODE, Renderer + + renderer = Renderer("noise renderer", debug_mode=DEBUG_MODE.ERROR) + renderer._ensure_root() + + toNoiseTex = Texture("noise-texture") + toNoiseTex.setup_2d_texture(width, height, Texture.T_unsigned_byte, Texture.F_r8i) + toNoiseTex.set_clear_color((0, 0, 0, 0)) + + node = ComputeNode("simplex") + node.add_dispatch(width // 16, height // 16, 1) + node_path = renderer._root_np.attach_new_node(node) + + with pkg_resources.path(glsl, "simplex.comp") as simplex_shader: + shader = Shader.load_compute(Shader.SL_GLSL, str(simplex_shader.absolute())) + node_path.set_shader(shader) + node_path.set_shader_input("toNoise", toNoiseTex) + + sattr = node_path.getAttrib(ShaderAttrib) + # renderer.render() + gsg = renderer._showbase_instance.win.get_gsg() + assert gsg.get_supports_compute_shaders(), f"renderer {gsg.get_class_type().name}" + renderer._showbase_instance.graphics_engine.dispatch_compute( + (32, 32, 1), sattr, gsg + ) + + def generate_simplex( - width, - height, + width: int, + height: int, seed, shift: Tuple[float, float], octaves: float = 2, @@ -179,8 +223,8 @@ def generate_perlin_file( description="Utility to export mesh files to bytemap.", ) parser.add_argument("output_path", help="where to write the bytemap file", type=str) - parser.add_argument("--width", help="the width pixels", type=int, default=100) - parser.add_argument("--height", help="the height pixels", type=int, default=100) + parser.add_argument("--width", help="the width pixels", type=int, default=256) + parser.add_argument("--height", help="the height pixels", type=int, default=256) parser.add_argument( "--smooth_iterations", help="smooth the output", type=int, default=0 ) @@ -202,12 +246,19 @@ def generate_perlin_file( if args.match_file_dimensions != "": width, height = get_image_dimensions(args.match_file_dimensions) - generate_perlin_file( - args.output_path, + # generate_perlin_file( + # args.output_path, + # width, + # height, + # args.smooth_iterations, + # args.seed, + # args.table_dim, + # args.shift, + # ) + + generate_simplex_p3d_gpu( width, height, - args.smooth_iterations, args.seed, - args.table_dim, args.shift, )