feat:3D multi dot
This commit is contained in:
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.5)
|
||||
project(base-project)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
find_package(X11 REQUIRED)
|
||||
find_package(OpenGL REQUIRED)
|
||||
add_executable(
|
||||
${PROJECT_NAME}
|
||||
main.cpp
|
||||
@@ -16,13 +16,31 @@ target_link_libraries(
|
||||
${PROJECT_NAME}
|
||||
PRIVATE
|
||||
libglfw3.a
|
||||
${X11_LIBRARIES}
|
||||
${OPENGL_LIBRARIES}
|
||||
GL
|
||||
Xrandr
|
||||
Xi
|
||||
Xinerama
|
||||
Xcursor
|
||||
pthread
|
||||
dl
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
target_link_libraries(
|
||||
${PROJECT_NAME}
|
||||
PRIVATE
|
||||
opengl32
|
||||
gdi32
|
||||
user32
|
||||
shell32
|
||||
winmm
|
||||
)
|
||||
else()
|
||||
find_package(X11 REQUIRED)
|
||||
target_link_libraries(
|
||||
${PROJECT_NAME}
|
||||
PRIVATE
|
||||
${X11_LIBRARIES}
|
||||
GL
|
||||
Xrandr
|
||||
Xi
|
||||
Xinerama
|
||||
Xcursor
|
||||
pthread
|
||||
dl
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -26,3 +26,86 @@ make -j
|
||||
```
|
||||
|
||||
目前用 `update_demo_values` 生成简单波纹示例数据,如需接入传感器数据,替换 main.cpp 里的该函数并在循环前设置好 `set_spec` / `set_panel_size`。
|
||||
|
||||
## Heatmap Demo (Heightmap + Dented Surface)
|
||||
|
||||
This demo renders a thick slab with a dented top surface based on sensor values.
|
||||
Values are mapped to a depth range (0-500 -> 0..kDentMax), so higher force makes
|
||||
the surface go deeper. The side walls follow the dent so it reads as one solid.
|
||||
|
||||
### UML (Mermaid)
|
||||
```mermaid
|
||||
classDiagram
|
||||
class AppState {
|
||||
+int minV
|
||||
+int maxV
|
||||
+mat4 mvp
|
||||
+vec3 cameraPos
|
||||
}
|
||||
class HeightMap {
|
||||
+low_values[12x7]
|
||||
+height_values[1200x700]
|
||||
+update_demo_heightmap(t)
|
||||
+upload_height_texture()
|
||||
}
|
||||
class HeatmapMesh {
|
||||
+meshCols
|
||||
+meshRows
|
||||
+vao/vbo/ibo
|
||||
}
|
||||
class SkirtMesh {
|
||||
+border_uvs
|
||||
+update_skirt_vertices()
|
||||
}
|
||||
class BaseMesh {
|
||||
+thickness
|
||||
+vao/vbo/ibo
|
||||
}
|
||||
class Renderer {
|
||||
+render_background()
|
||||
+render_base()
|
||||
+render_skirt()
|
||||
+render_heatmap()
|
||||
}
|
||||
class Shaders {
|
||||
+heatmap.vert/frag
|
||||
+base.vert/frag
|
||||
+bg.vert/frag
|
||||
}
|
||||
|
||||
AppState --> Renderer
|
||||
HeightMap --> HeatmapMesh
|
||||
HeightMap --> SkirtMesh
|
||||
Renderer --> HeatmapMesh
|
||||
Renderer --> SkirtMesh
|
||||
Renderer --> BaseMesh
|
||||
Renderer --> Shaders
|
||||
```
|
||||
|
||||
### How It Works (Step-by-Step)
|
||||
1) `update_demo_heightmap(t)` generates a slow-changing 12x7 grid using a few
|
||||
Gaussian blobs. This is the "raw sensor" layer.
|
||||
2) The 12x7 grid is upsampled to 1200x700 with bilinear filtering, stored in
|
||||
`height_values`.
|
||||
3) `height_values` is uploaded into an `GL_R32F` texture (`height_tex`).
|
||||
4) `heatmap.vert` samples `height_tex` and displaces the surface in Z:
|
||||
`z = uBaseZ + h`, where `h` is the mapped depth (0..kDentMax).
|
||||
5) `heatmap.frag` re-samples neighbor texels to approximate a normal, then applies
|
||||
a simple light + color ramp for readability.
|
||||
6) `init_skirt_geometry()` builds a ring of border vertices, and
|
||||
`update_skirt_vertices()` samples the border heights so the side walls "follow"
|
||||
the dent. This keeps the slab looking solid when the edge is depressed.
|
||||
7) Render order: background grid -> back face -> skirt -> heatmap surface.
|
||||
|
||||
### Key Parameters You Can Tune
|
||||
- `kThickness`: slab thickness.
|
||||
- `kDentMax`: maximum dent depth (keep <= thickness).
|
||||
- `kMeshCols/kMeshRows`: heatmap mesh resolution (visual smoothness).
|
||||
- `kHeightW/kHeightH`: texture resolution (sampling detail).
|
||||
- `g_state.minV/maxV`: value range (0..500 by default).
|
||||
|
||||
### If You Want Real Data
|
||||
Replace `update_demo_heightmap()` with your sensor values:
|
||||
- Fill `low_values` from your 12x7 data.
|
||||
- Run the same upsample step to `height_values`.
|
||||
- Call `upload_height_texture_if_needed()` and `update_skirt_vertices()` each frame.
|
||||
|
||||
1653
test/onlygl/main.cpp
1653
test/onlygl/main.cpp
File diff suppressed because it is too large
Load Diff
25
test/onlygl/shaders/base.frag
Normal file
25
test/onlygl/shaders/base.frag
Normal file
@@ -0,0 +1,25 @@
|
||||
#version 330 core
|
||||
|
||||
in vec3 vNormal;
|
||||
in vec3 vWorldPos;
|
||||
out vec4 FragColor;
|
||||
|
||||
uniform vec3 uCameraPos;
|
||||
uniform vec3 uLightDir;
|
||||
uniform vec3 uColor;
|
||||
|
||||
float saturate(float x) { return clamp(x, 0.0, 1.0); }
|
||||
|
||||
void main() {
|
||||
vec3 N = normalize(vNormal);
|
||||
vec3 L = normalize(uLightDir);
|
||||
vec3 V = normalize(uCameraPos - vWorldPos);
|
||||
vec3 H = normalize(L + V);
|
||||
|
||||
float diff = saturate(dot(N, L));
|
||||
float spec = pow(saturate(dot(N, H)), 24.0);
|
||||
|
||||
vec3 col = uColor * (0.35 + 0.65 * diff);
|
||||
col += vec3(1.0) * spec * 0.08;
|
||||
FragColor = vec4(clamp(col, 0.0, 1.0), 1.0);
|
||||
}
|
||||
15
test/onlygl/shaders/base.vert
Normal file
15
test/onlygl/shaders/base.vert
Normal file
@@ -0,0 +1,15 @@
|
||||
#version 330 core
|
||||
|
||||
layout(location = 0) in vec3 aPos;
|
||||
layout(location = 1) in vec3 aNormal;
|
||||
|
||||
out vec3 vNormal;
|
||||
out vec3 vWorldPos;
|
||||
|
||||
uniform mat4 uMVP;
|
||||
|
||||
void main() {
|
||||
vNormal = aNormal;
|
||||
vWorldPos = aPos;
|
||||
gl_Position = uMVP * vec4(aPos, 1.0);
|
||||
}
|
||||
65
test/onlygl/shaders/heatmap.frag
Normal file
65
test/onlygl/shaders/heatmap.frag
Normal file
@@ -0,0 +1,65 @@
|
||||
#version 330 core
|
||||
|
||||
in vec2 vUV;
|
||||
in vec3 vWorldPos;
|
||||
out vec4 FragColor;
|
||||
|
||||
uniform sampler2D uHeightTex;
|
||||
uniform float uMinV;
|
||||
uniform float uMaxV;
|
||||
uniform float uHeightScale;
|
||||
uniform vec2 uTexelSize;
|
||||
uniform vec2 uPlaneSize;
|
||||
uniform vec3 uCameraPos;
|
||||
uniform vec3 uLightDir;
|
||||
|
||||
float saturate(float x) { return clamp(x, 0.0, 1.0); }
|
||||
|
||||
float value01(float v) {
|
||||
return saturate((v - uMinV) / max(uMaxV - uMinV, 1e-6));
|
||||
}
|
||||
|
||||
vec3 colorRamp(float t) {
|
||||
t = saturate(t);
|
||||
vec3 c0 = vec3(0.10, 0.15, 0.85);
|
||||
vec3 c1 = vec3(0.05, 0.75, 0.95);
|
||||
vec3 c2 = vec3(0.10, 0.90, 0.35);
|
||||
vec3 c3 = vec3(0.95, 0.90, 0.20);
|
||||
vec3 c4 = vec3(0.95, 0.25, 0.12);
|
||||
if (t < 0.25) return mix(c0, c1, t / 0.25);
|
||||
if (t < 0.50) return mix(c1, c2, (t - 0.25) / 0.25);
|
||||
if (t < 0.75) return mix(c2, c3, (t - 0.50) / 0.25);
|
||||
return mix(c3, c4, (t - 0.75) / 0.25);
|
||||
}
|
||||
|
||||
void main() {
|
||||
float vC = texture(uHeightTex, vUV).r;
|
||||
float t = value01(vC);
|
||||
vec3 base = colorRamp(t);
|
||||
|
||||
float vL = texture(uHeightTex, vUV - vec2(uTexelSize.x, 0.0)).r;
|
||||
float vR = texture(uHeightTex, vUV + vec2(uTexelSize.x, 0.0)).r;
|
||||
float vD = texture(uHeightTex, vUV - vec2(0.0, uTexelSize.y)).r;
|
||||
float vU = texture(uHeightTex, vUV + vec2(0.0, uTexelSize.y)).r;
|
||||
|
||||
float hL = value01(vL) * uHeightScale;
|
||||
float hR = value01(vR) * uHeightScale;
|
||||
float hD = value01(vD) * uHeightScale;
|
||||
float hU = value01(vU) * uHeightScale;
|
||||
|
||||
float dx = max(1e-6, uPlaneSize.x * uTexelSize.x);
|
||||
float dy = max(1e-6, uPlaneSize.y * uTexelSize.y);
|
||||
float dHx = (hR - hL) / (2.0 * dx);
|
||||
float dHy = (hU - hD) / (2.0 * dy);
|
||||
|
||||
vec3 N = normalize(vec3(-dHx, -dHy, 1.0));
|
||||
vec3 L = normalize(uLightDir);
|
||||
vec3 V = normalize(uCameraPos - vWorldPos);
|
||||
|
||||
float diff = saturate(dot(N, L));
|
||||
float spec = pow(saturate(dot(reflect(-L, N), V)), 20.0);
|
||||
|
||||
vec3 col = base * (0.50 + 0.50 * diff);
|
||||
col += vec3(1.0) * spec * 0.20;
|
||||
FragColor = vec4(clamp(col, 0.0, 1.0), 1.0);
|
||||
}
|
||||
27
test/onlygl/shaders/heatmap.vert
Normal file
27
test/onlygl/shaders/heatmap.vert
Normal file
@@ -0,0 +1,27 @@
|
||||
#version 330 core
|
||||
|
||||
layout(location = 0) in vec3 aPos;
|
||||
layout(location = 1) in vec2 aUV;
|
||||
|
||||
out vec2 vUV;
|
||||
out vec3 vWorldPos;
|
||||
|
||||
uniform mat4 uMVP;
|
||||
uniform sampler2D uHeightTex;
|
||||
uniform float uMinV;
|
||||
uniform float uMaxV;
|
||||
uniform float uHeightScale;
|
||||
uniform float uBaseZ;
|
||||
|
||||
float value01(float v) {
|
||||
return clamp((v - uMinV) / max(uMaxV - uMinV, 1e-6), 0.0, 1.0);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vUV = aUV;
|
||||
float v = texture(uHeightTex, aUV).r;
|
||||
float h = value01(v) * uHeightScale;
|
||||
vec3 world = aPos + vec3(0.0, 0.0, uBaseZ + h);
|
||||
vWorldPos = world;
|
||||
gl_Position = uMVP * vec4(world, 1.0);
|
||||
}
|
||||
Reference in New Issue
Block a user