### Summary Fills the flat cross-section where the clipping plane cuts a solid, so a clipped closed solid reads as a real slice instead of a hollow shell. For a volumetric input (a Linear_cell_complex) each volume's cross-section is filled in that volume's own color, so the inner volumes stay distinct at the cut. Updated per the mailing thread. The cap now follows Guillaume's suggestion to keep it out of the drawing functions and factorize the grouping in the Basic_viewer through `Graphics_scene`, and Guillaume's open-surface question is handled by a closed-input guard. Ready for review. ### How it works - **Stencil parity fill.** The clipped solid is drawn once into the stencil buffer with an even-odd (parity) rule, so an odd number of surface crossings marks the interior. Parity is winding-independent, so it handles an LCC's inconsistent face orientation. A quad on the clip plane is then filled where the stencil is odd. - **Per-fragment clip is enough.** The existing shader discard gives a clean cap edge and multisampling anti-aliases it, so no `gl_ClipDistance` path is needed. - **Per-volume grouping in `Graphics_scene`, over the single face buffer.** `Graphics_scene` gains `volume_begin`/`volume_end` and, per volume, a list of face indices into the single (de-duplicated) face buffer. A shared wall is one face referenced by both neighboring volumes, so each volume is still closed for the parity fill while no geometry is duplicated and the display buffer stays de-duplicated. The separate cap buffer (`POS_CAP_FACES`) from the first version is gone. - **The LCC drawer only groups.** It calls `volume_begin`/`volume_end` around each volume and assigns that volume's faces by index, using a small dart-to-face-index map so a shared wall resolves to the same face for both neighbors. The viewer's cap pass draws each volume's referenced faces into the stencil, then a quad in the volume color. - **Closed-input guard.** Parity is only defined for a closed surface, so an open group has no meaningful cap. The viewer detects closedness from the face buffer alone (an edge used by a single triangle means open), matching vertices by position, and skips the cap for an open group. This is computed once, lazily, on the first clip, and cached, so a mesh that is never clipped pays nothing. - **Surface-mesh fallback.** A non-volumetric input has no per-volume groups and falls back to filling the whole face buffer as one cap in a neutral grey (still subject to the closed guard). - The stencil buffer is requested in `init_ogl_context`. - Only the two solid clip modes are capped (solid-half-only, solid-half-wireframe). The transparent-half mode is left uncapped so the ghost stays visible. ### Changes from the first version (per review) - **Placement.** Guillaume asked to keep the cap out of the many drawing functions and factorize it in the Basic_viewer. Done: the grouping now lives in `Graphics_scene` (`volume_begin`/`volume_end`) and the per-volume cap pass is in the viewer; the LCC drawer only tags faces to volumes. - **No duplicated geometry.** The first version used a separate, non-de-duplicated cap buffer. Now a shared wall is a single face referenced by both volumes. - **Open surfaces.** Added the closed-input guard for Guillaume's boundary question. - **Always-on.** The prototype `K` toggle is removed; the cap is always on in the two solid clip modes. - **Lazy.** The closed check is computed on the first clip and cached, not eagerly on every draw. ### Open questions for review - **Other volumetric drawers.** The grouping is emitted by the LCC drawer. `Combinatorial_map` and `Generalized_map` could tag volumes the same way; happy to add that here or in a follow-up. - **Cap color.** The cap is the volume's own color, drawn flat so it stays distinct from the real lit faces. Keep it, or make it configurable? ### Performance The only per-mesh cost is the closed check, which runs over every face once on the first clip and is then cached. On the refined_elephant model (~89k faces) it is about 100 ms, one time. It uses ordered maps; if a much larger mesh ever makes that first-clip pause visible, the vertex and edge lookups can move to hashed maps. The stencil fill itself is one pass per volume per frame, fine for tens of volumes; a very large multi-volume complex would want the passes batched. ### Testing Tested on a single hexahedron, rows of sewn hexahedra sharing internal walls (2, 6, and 40 volumes), disjoint hexahedra, a flat open sheet (correctly skipped by the guard), and closed surface meshes including the dense refined_elephant (~89k faces) across all three clip modes. Each volume's cross-section fills in its own color with no alternation across shared walls, the surface mesh caps grey, an open surface is left uncapped, and the transparent-half mode stays uncapped. Before/after screenshots are on the CGAL wiki.
1097 lines
26 KiB
C++
1097 lines
26 KiB
C++
// Copyright (c) 2018 GeometryFactory Sarl (France).
|
|
// All rights reserved.
|
|
//
|
|
// This file is part of CGAL (www.cgal.org).
|
|
//
|
|
// $URL$
|
|
// $Id$
|
|
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
|
|
//
|
|
//
|
|
// Author(s) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr>
|
|
|
|
#ifndef CGAL_BASIC_SHADERS_H
|
|
#define CGAL_BASIC_SHADERS_H
|
|
|
|
#include <CGAL/license/GraphicsView.h>
|
|
|
|
namespace CGAL
|
|
{
|
|
//------------------------------------------------------------------------------
|
|
const char VERTEX_SOURCE_COLOR[]=R"DELIM(
|
|
#version 150
|
|
in highp vec3 a_Pos;
|
|
in highp vec3 a_Normal;
|
|
in mediump vec3 a_Color;
|
|
|
|
out highp vec4 vs_fP; // view space position
|
|
out highp vec4 ls_fP; // local space position
|
|
out highp vec3 fN;
|
|
out mediump vec4 fColor;
|
|
|
|
uniform highp mat4 u_Mvp;
|
|
uniform highp mat4 u_Mv;
|
|
uniform mediump float u_PointSize;
|
|
uniform mediump vec3 u_DefaultColor;
|
|
uniform bool u_UseDefaultColor;
|
|
|
|
void main(void)
|
|
{
|
|
fColor = vec4(a_Color, 1.0);
|
|
if (u_UseDefaultColor)
|
|
{
|
|
fColor = vec4(u_DefaultColor, 1.0);
|
|
}
|
|
|
|
vec4 pos = vec4(a_Pos, 1.0);
|
|
|
|
ls_fP = pos;
|
|
vs_fP = u_Mv * pos;
|
|
|
|
fN = mat3(u_Mv)* a_Normal;
|
|
|
|
gl_Position = u_Mvp * pos;
|
|
gl_PointSize = u_PointSize;
|
|
}
|
|
)DELIM";
|
|
|
|
const char FRAGMENT_SOURCE_COLOR[]=R"DELIM(
|
|
#version 150
|
|
in highp vec4 vs_fP;
|
|
in highp vec4 ls_fP;
|
|
in highp vec3 fN;
|
|
in mediump vec4 fColor;
|
|
|
|
out mediump vec4 out_color;
|
|
|
|
uniform highp vec4 u_LightPos;
|
|
uniform mediump vec4 u_LightDiff;
|
|
uniform mediump vec4 u_LightSpec;
|
|
uniform mediump vec4 u_LightAmb;
|
|
uniform mediump float u_SpecPower;
|
|
|
|
uniform highp vec4 u_ClipPlane;
|
|
uniform highp vec4 u_PointPlane;
|
|
uniform mediump float u_RenderingMode;
|
|
uniform mediump float u_RenderingTransparency;
|
|
|
|
void main(void)
|
|
{
|
|
highp vec3 L = u_LightPos.xyz - vs_fP.xyz;
|
|
highp vec3 V = -vs_fP.xyz;
|
|
|
|
highp vec3 a_Normal = normalize(fN);
|
|
L = normalize(L);
|
|
V = normalize(V);
|
|
|
|
highp vec3 R = reflect(-L, a_Normal);
|
|
highp vec4 diffuse = vec4(max(dot(a_Normal,L), 0.0) * u_LightDiff.rgb * fColor.rgb, 1.0);
|
|
highp vec4 ambient = vec4(u_LightAmb.rgb * fColor.rgb, 1.0);
|
|
highp vec4 specular = pow(max(dot(R,V), 0.0), u_SpecPower) * u_LightSpec;
|
|
|
|
// onPlane == 1: inside clipping plane, should be solid;
|
|
// onPlane == -1: outside clipping plane, should be transparent;
|
|
// onPlane == 0: on clipping plane, whatever;
|
|
float onPlane = sign(dot((ls_fP.xyz-u_PointPlane.xyz), u_ClipPlane.xyz));
|
|
|
|
// rendering_mode == -1: draw all solid;
|
|
// rendering_mode == 0: draw solid only;
|
|
// rendering_mode == 1: draw transparent only;
|
|
if (u_RenderingMode == (onPlane+1)/2) {
|
|
// discard other than the corresponding half when rendering
|
|
discard;
|
|
}
|
|
|
|
// draw corresponding part
|
|
out_color = u_RenderingMode < 1 ? (diffuse + ambient) :
|
|
vec4(diffuse.rgb + ambient.rgb, u_RenderingTransparency);
|
|
}
|
|
)DELIM";
|
|
|
|
const char VERTEX_SOURCE_P_L[]=R"DELIM(
|
|
#version 150
|
|
in highp vec3 a_Pos;
|
|
in mediump vec3 a_Color;
|
|
|
|
out mediump vec4 fColor;
|
|
out highp vec4 ls_fP; // local space
|
|
|
|
uniform highp mat4 u_Mvp;
|
|
uniform mediump float u_PointSize;
|
|
uniform mediump vec3 u_DefaultColor;
|
|
uniform bool u_UseDefaultColor;
|
|
|
|
void main(void)
|
|
{
|
|
fColor = vec4(a_Color, 1.0);
|
|
if (u_UseDefaultColor)
|
|
{
|
|
fColor = vec4(u_DefaultColor, 1.0);
|
|
}
|
|
|
|
vec4 pos = vec4(a_Pos, 1.0);
|
|
|
|
ls_fP = pos;
|
|
|
|
gl_Position = u_Mvp * pos;
|
|
|
|
// u_PointSize is a constant screen-pixel diameter, the same in 2D and 3D and
|
|
// independent of the scene scale (the legacy glPointSize() semantics). We do
|
|
// not divide by the clip-space w: that foreshortened points with depth, so on
|
|
// a large scene the points shrank below one pixel and disappeared.
|
|
gl_PointSize = u_PointSize;
|
|
}
|
|
)DELIM";
|
|
|
|
const char VERTEX_SOURCE_SHAPE[]=R"DELIM(
|
|
#version 150
|
|
in highp vec3 a_Pos;
|
|
in mediump vec3 a_Color;
|
|
|
|
out mediump vec4 gColor;
|
|
out highp vec4 ls_fP;
|
|
|
|
uniform highp mat4 u_Mvp;
|
|
uniform mediump vec3 u_DefaultColor;
|
|
uniform bool u_UseDefaultColor;
|
|
|
|
void main(void)
|
|
{
|
|
gColor = vec4(a_Color, 1.0);
|
|
if (u_UseDefaultColor)
|
|
{
|
|
gColor = vec4(u_DefaultColor, 1.0);
|
|
}
|
|
|
|
gl_Position = vec4(a_Pos, 1.0);
|
|
}
|
|
)DELIM";
|
|
|
|
const char GEOMETRY_SOURCE_SPHERE[]=R"DELIM(
|
|
#version 150
|
|
layout(points) in;
|
|
layout(triangle_strip, max_vertices = 72) out; // max_vertices = (resolution+1) * 2 * latResolution
|
|
|
|
#define PI 3.14159265358979323846
|
|
|
|
in mediump vec4 gColor[];
|
|
|
|
out mediump vec4 fColor;
|
|
out highp vec4 ls_fP;
|
|
|
|
uniform highp mat4 u_Mvp;
|
|
uniform mediump float u_Radius;
|
|
|
|
void drawSphere(in vec4 center, in float radius, in float resolution)
|
|
{
|
|
float latResolution = resolution*0.5;
|
|
float stepTheta = PI/latResolution;
|
|
float stepPhi = 2*PI/resolution;
|
|
for(int i=0; i<latResolution; ++i)
|
|
{
|
|
float theta1 = stepTheta*i;
|
|
float theta2 = stepTheta*(i+1);
|
|
for(int j=0; j<=resolution; ++j)
|
|
{
|
|
float phi = stepPhi*j;
|
|
float x1 = center.x + radius * sin(theta1) * cos(phi);
|
|
float y1 = center.y + radius * sin(theta1) * sin(phi);
|
|
float z1 = center.z + radius * cos(theta1);
|
|
ls_fP = vec4(x1, y1, z1, 1.0);
|
|
gl_Position = u_Mvp * ls_fP;
|
|
EmitVertex();
|
|
|
|
float x2 = center.x + radius * sin(theta2) * cos(phi);
|
|
float y2 = center.y + radius * sin(theta2) * sin(phi);
|
|
float z2 = center.z + radius * cos(theta2);
|
|
ls_fP = vec4(x2, y2, z2, 1.0);
|
|
gl_Position = u_Mvp * ls_fP;
|
|
EmitVertex();
|
|
}
|
|
EndPrimitive();
|
|
}
|
|
}
|
|
|
|
void main(void)
|
|
{
|
|
fColor = gColor[0];
|
|
|
|
int resolution = 8;
|
|
vec4 center = gl_in[0].gl_Position;
|
|
|
|
drawSphere(center, u_Radius, resolution);
|
|
}
|
|
)DELIM";
|
|
|
|
// Join spheres for the tube-edge mode. Takes each edge (a line) and emits a
|
|
// sphere at both endpoints, so the spheres land exactly where the open cylinders
|
|
// meet. Drawing from the edges (not the scene's point set) means a point that is
|
|
// not a tube endpoint (e.g. a triangulation's infinite vertex) never gets a
|
|
// sphere, and a zero-length (degenerate) edge, whose cylinder is not drawn, is
|
|
// skipped so no sphere floats where there is no tube.
|
|
const char GEOMETRY_SOURCE_SPHERE_JOIN[]=R"DELIM(
|
|
#version 150
|
|
layout(lines) in;
|
|
layout(triangle_strip, max_vertices = 84) out; // 2 spheres * (resolution+1)*2*(resolution/2), resolution = 6
|
|
|
|
#define PI 3.14159265358979323846
|
|
|
|
in mediump vec4 gColor[];
|
|
|
|
out mediump vec4 fColor;
|
|
out highp vec4 ls_fP;
|
|
|
|
uniform highp mat4 u_Mvp;
|
|
uniform mediump float u_Radius;
|
|
|
|
void drawSphere(in vec4 center, in float radius, in float resolution)
|
|
{
|
|
float latResolution = resolution*0.5;
|
|
float stepTheta = PI/latResolution;
|
|
float stepPhi = 2*PI/resolution;
|
|
for(int i=0; i<latResolution; ++i)
|
|
{
|
|
float theta1 = stepTheta*i;
|
|
float theta2 = stepTheta*(i+1);
|
|
for(int j=0; j<=resolution; ++j)
|
|
{
|
|
float phi = stepPhi*j;
|
|
float x1 = center.x + radius * sin(theta1) * cos(phi);
|
|
float y1 = center.y + radius * sin(theta1) * sin(phi);
|
|
float z1 = center.z + radius * cos(theta1);
|
|
ls_fP = vec4(x1, y1, z1, 1.0);
|
|
gl_Position = u_Mvp * ls_fP;
|
|
EmitVertex();
|
|
|
|
float x2 = center.x + radius * sin(theta2) * cos(phi);
|
|
float y2 = center.y + radius * sin(theta2) * sin(phi);
|
|
float z2 = center.z + radius * cos(theta2);
|
|
ls_fP = vec4(x2, y2, z2, 1.0);
|
|
gl_Position = u_Mvp * ls_fP;
|
|
EmitVertex();
|
|
}
|
|
EndPrimitive();
|
|
}
|
|
}
|
|
|
|
void main(void)
|
|
{
|
|
vec4 a = gl_in[0].gl_Position;
|
|
vec4 b = gl_in[1].gl_Position;
|
|
|
|
// Skip degenerate (zero-length) edges: there is no cylinder there, so a join
|
|
// sphere would float where no tube is.
|
|
if (length(b.xyz - a.xyz) < 1e-7)
|
|
return;
|
|
|
|
// Each endpoint sphere takes that endpoint's color, so it matches the cylinder.
|
|
fColor = gColor[0];
|
|
drawSphere(a, u_Radius, 6.0);
|
|
fColor = gColor[1];
|
|
drawSphere(b, u_Radius, 6.0);
|
|
}
|
|
)DELIM";
|
|
|
|
const char GEOMETRY_SOURCE_CYLINDER[]=R"DELIM(
|
|
#version 150
|
|
layout(lines) in;
|
|
layout(triangle_strip, max_vertices = 22) out;
|
|
|
|
#define PI 3.14159265358979323846
|
|
|
|
in mediump vec4 gColor[];
|
|
|
|
out mediump vec4 fColor;
|
|
out highp vec4 ls_fP;
|
|
|
|
uniform highp mat4 u_Mvp;
|
|
uniform mediump float u_Radius;
|
|
|
|
void drawCylinder(in vec3 u, in vec3 v, in vec4 bot, in vec4 top, in float radius, in float resolution)
|
|
{
|
|
float step = 2*PI/resolution;
|
|
for(int i=0; i<=resolution; ++i)
|
|
{
|
|
float theta = step*i;
|
|
float cosf = radius*cos(theta);
|
|
float sinf = radius*sin(theta);
|
|
vec3 xAxis = cosf*u.xyz;
|
|
vec3 yAxis = sinf*v.xyz;
|
|
ls_fP = vec4(top.xyz+xAxis.xyz+yAxis.xyz, 1.0);
|
|
gl_Position = u_Mvp * ls_fP;
|
|
EmitVertex();
|
|
ls_fP = vec4(bot.xyz+xAxis.xyz+yAxis.xyz, 1.0);
|
|
gl_Position = u_Mvp * ls_fP;
|
|
EmitVertex();
|
|
}
|
|
EndPrimitive();
|
|
}
|
|
|
|
void main(void)
|
|
{
|
|
fColor = gColor[0];
|
|
|
|
vec4 a = gl_in[0].gl_Position;
|
|
vec4 b = gl_in[1].gl_Position;
|
|
|
|
vec3 n = normalize(vec3(b.x-a.x, b.y-a.y, b.z-a.z)); // compute top normal
|
|
|
|
vec3 w = normalize(vec3(-n.z, n.x, n.y));
|
|
|
|
// Axis vectors
|
|
vec3 u = normalize(cross(n, w));
|
|
vec3 v = normalize(cross(n, u));
|
|
|
|
int resolution = 10;
|
|
|
|
drawCylinder(u, v, a, b, u_Radius, resolution);
|
|
}
|
|
)DELIM";
|
|
|
|
const char FRAGMENT_SOURCE_P_L[]=R"DELIM(
|
|
#version 150
|
|
in mediump vec4 fColor;
|
|
in highp vec4 ls_fP;
|
|
|
|
out mediump vec4 out_color;
|
|
|
|
uniform highp vec4 u_ClipPlane;
|
|
uniform highp vec4 u_PointPlane;
|
|
uniform mediump float u_RenderingMode;
|
|
|
|
void main(void)
|
|
{
|
|
// onPlane == 1: inside clipping plane, should be solid;
|
|
// onPlane == -1: outside clipping plane, should be transparent;
|
|
// onPlane == 0: on clipping plane, whatever;
|
|
float onPlane = sign(dot((ls_fP.xyz-u_PointPlane.xyz), u_ClipPlane.xyz));
|
|
|
|
// rendering_mode == -1: draw both inside and outside;
|
|
// rendering_mode == 0: draw inside only;
|
|
// rendering_mode == 1: draw outside only;
|
|
if (u_RenderingMode == (onPlane+1)/2) {
|
|
// discard other than the corresponding half when rendering
|
|
discard;
|
|
}
|
|
|
|
out_color = fColor;
|
|
}
|
|
)DELIM";
|
|
|
|
const char VERTEX_SOURCE_CLIPPING_PLANE[]=R"DELIM(
|
|
#version 150
|
|
in highp vec3 a_Pos;
|
|
|
|
uniform highp mat4 u_Vp;
|
|
uniform highp mat4 u_M;
|
|
|
|
void main(void)
|
|
{
|
|
gl_Position = u_Vp * u_M * vec4(a_Pos, 1.0);
|
|
}
|
|
)DELIM";
|
|
|
|
const char FRAGMENT_SOURCE_CLIPPING_PLANE[]=R"DELIM(
|
|
#version 150
|
|
|
|
out mediump vec4 out_color;
|
|
|
|
uniform mediump vec4 u_Color;
|
|
|
|
void main(void)
|
|
{
|
|
out_color = u_Color;
|
|
}
|
|
)DELIM";
|
|
|
|
const char VERTEX_SOURCE_LINE[]=R"DELIM(
|
|
#version 150
|
|
in highp vec3 a_Pos;
|
|
in mediump vec3 a_Color;
|
|
|
|
out VS_OUT {
|
|
mediump vec4 color;
|
|
} vs_out; // vertex shader output
|
|
|
|
uniform mediump vec3 u_DefaultColor;
|
|
uniform bool u_UseDefaultColor;
|
|
|
|
void main(void)
|
|
{
|
|
vs_out.color = vec4(a_Color, 1.0);
|
|
if (u_UseDefaultColor)
|
|
{
|
|
vs_out.color = vec4(u_DefaultColor, 1.0);
|
|
}
|
|
|
|
gl_Position = vec4(a_Pos, 1.0);
|
|
}
|
|
)DELIM";
|
|
|
|
const char GEOMETRY_SOURCE_ARROW[]=R"DELIM(
|
|
#version 150
|
|
layout(lines) in;
|
|
layout(triangle_strip, max_vertices = 82) out; // max_vertices = resolution * 2 + 2 (cylinder) + resolution * 3 (disc) + resolution * 3 (cone)
|
|
|
|
#define PI 3.14159265358979323846
|
|
|
|
in VS_OUT {
|
|
mediump vec4 color;
|
|
} gs_in[]; // geometry shader input
|
|
|
|
out mediump vec4 fColor;
|
|
|
|
uniform highp mat4 u_Mvp;
|
|
uniform mediump float u_SceneRadius;
|
|
|
|
void drawTriangle(in vec4 v1, in vec4 v2, in vec4 v3)
|
|
{
|
|
gl_Position = u_Mvp*v1;
|
|
EmitVertex();
|
|
gl_Position = u_Mvp*v2;
|
|
EmitVertex();
|
|
gl_Position = u_Mvp*v3;
|
|
EmitVertex();
|
|
EndPrimitive();
|
|
}
|
|
|
|
void drawTriangleFan(in vec3 u, in vec3 v, in vec4 center, in vec4 edge0, in float radius, in int resolution)
|
|
{
|
|
float step = 2*PI/resolution;
|
|
for(int i=0; i<resolution; ++i)
|
|
{
|
|
float theta = step*i;
|
|
float cosf = radius*cos(theta);
|
|
float sinf = radius*sin(theta);
|
|
vec3 xAxis = cosf*u.xyz;
|
|
vec3 yAxis = sinf*v.xyz;
|
|
vec4 edge1 = vec4(edge0.xyz+xAxis.xyz+yAxis.xyz, 1.0);
|
|
theta = step*(i+1);
|
|
cosf = radius*cos(theta);
|
|
sinf = radius*sin(theta);
|
|
xAxis = cosf*u.xyz;
|
|
yAxis = sinf*v.xyz;
|
|
vec4 edge2 = vec4(edge0.xyz+xAxis.xyz+yAxis.xyz, 1.0);
|
|
drawTriangle(center, edge1, edge2);
|
|
}
|
|
}
|
|
|
|
void drawDisc(in vec3 u, in vec3 v, in vec4 center, in float radius, in int resolution)
|
|
{
|
|
drawTriangleFan(u, v, center, center, radius, resolution);
|
|
}
|
|
|
|
void drawCone(in vec3 u, in vec3 v, in vec3 n, in vec4 center, in float radius, in float height, in int resolution)
|
|
{
|
|
drawTriangleFan(u, v, center, vec4(center.xyz-height*n.xyz, 1.0), radius, resolution);
|
|
}
|
|
|
|
void drawCylinder(in vec3 u, in vec3 v, in vec4 bot, in vec4 top, in float radius, in float resolution)
|
|
{
|
|
float step = 2*PI/resolution;
|
|
for(int i=0; i<=resolution; ++i)
|
|
{
|
|
float theta = step*i;
|
|
float cosf = radius*cos(theta);
|
|
float sinf = radius*sin(theta);
|
|
vec3 xAxis = cosf*u.xyz;
|
|
vec3 yAxis = sinf*v.xyz;
|
|
gl_Position = u_Mvp * vec4(top.xyz+xAxis.xyz+yAxis.xyz, 1.0);
|
|
EmitVertex();
|
|
gl_Position = u_Mvp * vec4(bot.xyz+xAxis.xyz+yAxis.xyz, 1.0);
|
|
EmitVertex();
|
|
}
|
|
EndPrimitive();
|
|
}
|
|
|
|
void main(void)
|
|
{
|
|
fColor = gs_in[0].color;
|
|
|
|
vec4 a = gl_in[0].gl_Position;
|
|
vec4 b = gl_in[1].gl_Position;
|
|
|
|
vec3 n = normalize(vec3(b.x-a.x, b.y-a.y, b.z-a.z)); // compute top normal
|
|
vec3 w = normalize(vec3(-n.z, n.x, n.y));
|
|
|
|
// Axis vectors
|
|
vec3 u = normalize(cross(n, w));
|
|
vec3 v = normalize(cross(n, u));
|
|
|
|
float radius = 0.013 * u_SceneRadius;
|
|
float height = 0.035 * u_SceneRadius;
|
|
int resolution = 10;
|
|
|
|
vec4 c = vec4(b.xyz-height*n.xyz, 1.0);
|
|
drawDisc(u, v, c, radius, resolution);
|
|
drawCone(u, v, n, b, radius, height, resolution);
|
|
drawCylinder(u, v, a, c, radius*0.5, resolution);
|
|
}
|
|
)DELIM";
|
|
|
|
const char GEOMETRY_SOURCE_LINE[]=R"DELIM(
|
|
#version 150
|
|
layout(lines) in;
|
|
layout(line_strip, max_vertices = 2) out;
|
|
|
|
in VS_OUT {
|
|
mediump vec4 color;
|
|
} gs_in[]; // geometry shader input
|
|
|
|
out mediump vec4 fColor;
|
|
|
|
uniform highp mat4 u_Mvp;
|
|
|
|
void main(void)
|
|
{
|
|
fColor = gs_in[0].color;
|
|
|
|
gl_Position = u_Mvp * gl_in[0].gl_Position;
|
|
EmitVertex();
|
|
gl_Position = u_Mvp * gl_in[1].gl_Position;
|
|
EmitVertex();
|
|
|
|
EndPrimitive();
|
|
}
|
|
)DELIM";
|
|
|
|
const char FRAGMENT_SOURCE_LINE[]=R"DELIM(
|
|
#version 150
|
|
|
|
in mediump vec4 fColor;
|
|
|
|
out mediump vec4 out_color;
|
|
|
|
void main(void)
|
|
{
|
|
out_color = fColor;
|
|
}
|
|
)DELIM";
|
|
|
|
const char VERTEX_SOURCE_NORMAL[]=R"DELIM(
|
|
#version 150
|
|
in highp vec3 a_Pos;
|
|
in highp vec3 a_Normal;
|
|
|
|
out VS_OUT {
|
|
mediump vec4 color;
|
|
highp vec3 normal;
|
|
} vs_out; // vertex shader output
|
|
|
|
uniform highp mat4 u_Mv;
|
|
uniform mediump vec3 u_DefaultColor;
|
|
uniform bool u_UseDefaultColor;
|
|
|
|
void main(void)
|
|
{
|
|
vs_out.color = vec4(abs(normalize(a_Normal)), 1.0);
|
|
if (u_UseDefaultColor)
|
|
{
|
|
vs_out.color = vec4(u_DefaultColor, 1.0);
|
|
}
|
|
|
|
mat3 normalMatrix = mat3(transpose(inverse(u_Mv)));
|
|
vs_out.normal = normalize(vec3(vec4(normalMatrix * a_Normal, 0.0)));
|
|
|
|
gl_Position = vec4(a_Pos, 1.0);
|
|
}
|
|
)DELIM";
|
|
|
|
const char GEOMETRY_SOURCE_NORMAL[]=R"DELIM(
|
|
#version 150
|
|
layout (triangles) in;
|
|
layout (line_strip, max_vertices = 6) out;
|
|
|
|
in VS_OUT {
|
|
mediump vec4 color;
|
|
highp vec3 normal;
|
|
} gs_in[]; // geometry shader input
|
|
|
|
out mediump vec4 fColor;
|
|
out highp vec4 ls_fP;
|
|
|
|
uniform highp mat4 u_Projection;
|
|
uniform mediump float u_Factor;
|
|
uniform mediump float u_SceneRadius;
|
|
uniform highp mat4 u_Mv;
|
|
uniform bool u_DisplayFaceNormal;
|
|
|
|
void GenerateLine(int index)
|
|
{
|
|
fColor = gs_in[index].color;
|
|
|
|
ls_fP = gl_in[index].gl_Position;
|
|
gl_Position = u_Projection * u_Mv * gl_in[index].gl_Position;
|
|
EmitVertex();
|
|
|
|
vec4 newPosition = u_Mv * gl_in[index].gl_Position + vec4(gs_in[index].normal, 0.0) * u_SceneRadius * u_Factor;
|
|
ls_fP = inverse(u_Mv) * newPosition;
|
|
gl_Position = u_Projection * newPosition;
|
|
EmitVertex();
|
|
|
|
EndPrimitive();
|
|
}
|
|
|
|
void DrawVerticesNormal()
|
|
{
|
|
GenerateLine(0); // first vertex normal
|
|
GenerateLine(1); // second vertex normal
|
|
GenerateLine(2); // third vertex normal
|
|
}
|
|
|
|
void DrawFaceNormal()
|
|
{
|
|
fColor = (gs_in[0].color + gs_in[1].color + gs_in[2].color) / 3;
|
|
vec4 center = (gl_in[0].gl_Position + gl_in[1].gl_Position + gl_in[2].gl_Position) / 3;
|
|
ls_fP = center;
|
|
gl_Position = u_Projection * u_Mv * center;
|
|
EmitVertex();
|
|
|
|
vec3 n = normalize((gs_in[0].normal.xyz + gs_in[1].normal.xyz + gs_in[2].normal.xyz) / 3);
|
|
|
|
vec4 newPosition = u_Mv * center + vec4(n, 0.0) * u_SceneRadius * u_Factor;
|
|
ls_fP = inverse(u_Mv) * newPosition;
|
|
gl_Position = u_Projection * newPosition;
|
|
EmitVertex();
|
|
}
|
|
|
|
void main()
|
|
{
|
|
if (u_DisplayFaceNormal)
|
|
{
|
|
DrawFaceNormal();
|
|
}
|
|
else
|
|
{
|
|
DrawVerticesNormal();
|
|
}
|
|
}
|
|
)DELIM";
|
|
|
|
const char VERTEX_SOURCE_TRIANGLE[]=R"DELIM(
|
|
#version 150
|
|
in highp vec3 a_Pos;
|
|
|
|
out VS_OUT {
|
|
mediump vec4 color;
|
|
highp vec4 ls_fP;
|
|
} vs_out;
|
|
|
|
uniform highp mat4 u_Mvp;
|
|
|
|
void main(void)
|
|
{
|
|
vec4 pos = vec4(a_Pos, 1.0);
|
|
|
|
vs_out.color = vec4(0.85, 0.85, 0.85, 1.0);
|
|
vs_out.ls_fP = pos;
|
|
|
|
gl_Position = u_Mvp * pos;
|
|
}
|
|
)DELIM";
|
|
|
|
const char GEOMETRY_SOURCE_TRIANGLE[]=R"DELIM(
|
|
#version 150
|
|
layout (triangles) in;
|
|
layout (line_strip, max_vertices=4) out;
|
|
|
|
in VS_OUT {
|
|
mediump vec4 color;
|
|
highp vec4 ls_fP;
|
|
} gs_in[];
|
|
|
|
out mediump vec4 fColor;
|
|
out highp vec4 ls_fP;
|
|
|
|
void main(void)
|
|
{
|
|
fColor = gs_in[0].color;
|
|
ls_fP = gs_in[0].ls_fP;
|
|
gl_Position = gl_in[0].gl_Position;
|
|
EmitVertex();
|
|
|
|
ls_fP = gs_in[1].ls_fP;
|
|
gl_Position = gl_in[1].gl_Position;
|
|
EmitVertex();
|
|
|
|
ls_fP = gs_in[2].ls_fP;
|
|
gl_Position = gl_in[2].gl_Position;
|
|
EmitVertex();
|
|
|
|
ls_fP = gs_in[0].ls_fP;
|
|
gl_Position = gl_in[0].gl_Position;
|
|
EmitVertex();
|
|
}
|
|
)DELIM";
|
|
|
|
const char VERTEX_SOURCE_LINE_WIDTH[]=R"DELIM(
|
|
#version 150
|
|
|
|
in highp vec3 a_Pos;
|
|
in mediump vec3 a_Color;
|
|
|
|
out VS_OUT {
|
|
mediump float pointSize;
|
|
mediump vec4 color;
|
|
highp vec4 ls_fP;
|
|
} vs_out;
|
|
|
|
uniform highp mat4 u_Mvp;
|
|
uniform mediump float u_PointSize;
|
|
uniform mediump vec3 u_DefaultColor;
|
|
uniform bool u_UseDefaultColor;
|
|
|
|
void main(void)
|
|
{
|
|
vec4 pos = vec4(a_Pos, 1.0);
|
|
|
|
vs_out.ls_fP = pos;
|
|
vs_out.color = vec4(a_Color, 1.0);
|
|
if (u_UseDefaultColor)
|
|
{
|
|
vs_out.color = vec4(u_DefaultColor, 1.0);
|
|
}
|
|
|
|
gl_Position = u_Mvp * pos;
|
|
|
|
// u_PointSize is a constant screen-pixel width, the same in 2D and 3D and
|
|
// independent of the scene scale (the legacy glLineWidth() semantics). We do
|
|
// not divide by the clip-space w here: doing so foreshortened the width with
|
|
// depth, which made far edges collapse below one pixel and made the +/- step
|
|
// depend on the scene scale.
|
|
vs_out.pointSize = u_PointSize;
|
|
}
|
|
)DELIM";
|
|
|
|
const char GEOMETRY_SOURCE_LINE_WIDTH[]=R"DELIM(
|
|
#version 150
|
|
layout (lines) in;
|
|
layout (triangle_strip, max_vertices = 4) out;
|
|
|
|
in VS_OUT {
|
|
mediump float pointSize;
|
|
mediump vec4 color;
|
|
highp vec4 ls_fP;
|
|
} gs_in[];
|
|
|
|
out mediump vec4 fColor;
|
|
out highp vec4 ls_fP;
|
|
|
|
uniform mediump float u_PointSize;
|
|
uniform mediump vec2 u_Viewport;
|
|
|
|
// clip space -> viewport pixels
|
|
vec2 to_pixel(vec4 clip)
|
|
{
|
|
return ((clip.xy / clip.w) * 0.5 + 0.5) * u_Viewport;
|
|
}
|
|
|
|
// viewport pixels -> clip space, reusing one endpoint's depth (z, w) so the quad
|
|
// keeps perspective-correct depth without any inverse(u_Mvp)
|
|
vec4 to_clip(vec2 px, vec4 endpoint)
|
|
{
|
|
vec2 ndc = (px / u_Viewport) * 2.0 - 1.0;
|
|
return vec4(ndc * endpoint.w, endpoint.z, endpoint.w);
|
|
}
|
|
|
|
void emit(vec2 px, vec4 endpoint, int i)
|
|
{
|
|
gl_Position = to_clip(px, endpoint);
|
|
fColor = gs_in[i].color;
|
|
ls_fP = gs_in[i].ls_fP;
|
|
EmitVertex();
|
|
}
|
|
|
|
void main(void)
|
|
{
|
|
vec4 c0 = gl_in[0].gl_Position;
|
|
vec4 c1 = gl_in[1].gl_Position;
|
|
|
|
// skip degenerate clip positions to avoid a division by zero
|
|
if (c0.w == 0.0 || c1.w == 0.0)
|
|
return;
|
|
|
|
vec2 p0 = to_pixel(c0);
|
|
vec2 p1 = to_pixel(c1);
|
|
|
|
vec2 edge = p1 - p0;
|
|
float segLen = length(edge);
|
|
vec2 dir = (segLen > 1e-5) ? edge / segLen : vec2(1.0, 0.0);
|
|
vec2 perp = vec2(-dir.y, dir.x);
|
|
|
|
// Half-width in pixels. u_PointSize (carried in pointSize) is the full edge
|
|
// width in screen pixels, so the per-side offset is half of it, averaged over
|
|
// the two endpoints. Clamp to a minimum so a very thin edge still keeps a solid
|
|
// one-pixel core instead of disappearing.
|
|
float halfWidth = max(0.5 * 0.5 * (gs_in[0].pointSize + gs_in[1].pointSize), 1.0);
|
|
|
|
// Opaque square-capped edge: emit the exact rectangle from p0 to p1, half the
|
|
// width on each side. The edge is drawn solid and the framebuffer multisampling
|
|
// smooths the silhouette, so it stays fully opaque at any width: there is no
|
|
// per-pixel blending, so a thin edge cannot fade into the background.
|
|
vec2 dperp = perp * halfWidth;
|
|
|
|
emit(p0 - dperp, c0, 0);
|
|
emit(p0 + dperp, c0, 0);
|
|
emit(p1 - dperp, c1, 1);
|
|
emit(p1 + dperp, c1, 1);
|
|
}
|
|
)DELIM";
|
|
|
|
const char FRAGMENT_SOURCE_LINE_WIDTH[]=R"DELIM(
|
|
#version 150
|
|
in mediump vec4 fColor;
|
|
in highp vec4 ls_fP;
|
|
|
|
out mediump vec4 out_color;
|
|
|
|
uniform highp vec4 u_ClipPlane;
|
|
uniform highp vec4 u_PointPlane;
|
|
uniform mediump float u_RenderingMode;
|
|
|
|
void main(void)
|
|
{
|
|
// onPlane == 1: inside clipping plane, should be solid;
|
|
// onPlane == -1: outside clipping plane, should be transparent;
|
|
// onPlane == 0: on clipping plane, whatever;
|
|
float onPlane = sign(dot((ls_fP.xyz-u_PointPlane.xyz), u_ClipPlane.xyz));
|
|
|
|
// rendering_mode == -1: draw both inside and outside;
|
|
// rendering_mode == 0: draw inside only;
|
|
// rendering_mode == 1: draw outside only;
|
|
if (u_RenderingMode == (onPlane+1)/2) {
|
|
discard;
|
|
}
|
|
|
|
// Opaque edge: output the solid edge color. The framebuffer multisampling
|
|
// smooths the silhouette, so the edge stays fully solid at any width without
|
|
// any per-pixel blending.
|
|
out_color = fColor;
|
|
}
|
|
)DELIM";
|
|
|
|
// Vertex-disk join for the flat edges. Takes each edge (a line) and emits a small
|
|
// screen-space disk at both endpoints, in the edge color and at the edge width,
|
|
// so the square-cap corners where flat edges meet are filled. It reuses the same
|
|
// pixel-to-clip conversion as the wide-edge shader, so the disk radius matches the
|
|
// edge half-width. Drawing from the edges (not the point set) skips degenerate
|
|
// edges and never touches a non-edge point, so no disk floats where there is no
|
|
// edge. The disk is screen-facing (a rounded quad), not a sphere, so it stays flat
|
|
// in 3D and scales at any width with no point-size limit.
|
|
const char GEOMETRY_SOURCE_EDGE_DISK[]=R"DELIM(
|
|
#version 150
|
|
layout(lines) in;
|
|
layout(triangle_strip, max_vertices = 8) out; // 2 endpoints * 4 corners
|
|
|
|
in mediump vec4 gColor[];
|
|
|
|
out mediump vec4 fColor;
|
|
out highp vec4 ls_fP;
|
|
out mediump vec2 v_coord;
|
|
|
|
uniform highp mat4 u_Mvp;
|
|
uniform mediump float u_PointSize;
|
|
uniform mediump vec2 u_Viewport;
|
|
|
|
// clip space -> viewport pixels
|
|
vec2 to_pixel(vec4 clip)
|
|
{
|
|
return ((clip.xy / clip.w) * 0.5 + 0.5) * u_Viewport;
|
|
}
|
|
|
|
// viewport pixels -> clip space, reusing the endpoint depth (z, w)
|
|
vec4 to_clip(vec2 px, vec4 endpoint)
|
|
{
|
|
vec2 ndc = (px / u_Viewport) * 2.0 - 1.0;
|
|
return vec4(ndc * endpoint.w, endpoint.z, endpoint.w);
|
|
}
|
|
|
|
void emitDisk(vec4 clip, vec4 modelPos, vec4 color, float radius)
|
|
{
|
|
vec2 c = to_pixel(clip);
|
|
fColor = color;
|
|
ls_fP = modelPos;
|
|
|
|
v_coord = vec2(-1.0, -1.0); gl_Position = to_clip(c + vec2(-radius, -radius), clip); EmitVertex();
|
|
v_coord = vec2(-1.0, 1.0); gl_Position = to_clip(c + vec2(-radius, radius), clip); EmitVertex();
|
|
v_coord = vec2( 1.0, -1.0); gl_Position = to_clip(c + vec2( radius, -radius), clip); EmitVertex();
|
|
v_coord = vec2( 1.0, 1.0); gl_Position = to_clip(c + vec2( radius, radius), clip); EmitVertex();
|
|
EndPrimitive();
|
|
}
|
|
|
|
void main(void)
|
|
{
|
|
vec4 m0 = gl_in[0].gl_Position; // model-space position (a_Pos), from VERTEX_SOURCE_SHAPE
|
|
vec4 m1 = gl_in[1].gl_Position;
|
|
|
|
// Skip degenerate (zero-length) edges: no flat edge there, so no corner to fill.
|
|
if (length(m1.xyz - m0.xyz) < 1e-7)
|
|
return;
|
|
|
|
vec4 clip0 = u_Mvp * m0;
|
|
vec4 clip1 = u_Mvp * m1;
|
|
if (clip0.w == 0.0 || clip1.w == 0.0)
|
|
return;
|
|
|
|
// Same half-width as the wide edge: half the edge width in pixels, min one pixel.
|
|
float radius = max(0.5 * u_PointSize, 1.0);
|
|
|
|
emitDisk(clip0, m0, gColor[0], radius);
|
|
emitDisk(clip1, m1, gColor[1], radius);
|
|
}
|
|
)DELIM";
|
|
|
|
const char FRAGMENT_SOURCE_EDGE_DISK[]=R"DELIM(
|
|
#version 150
|
|
in mediump vec4 fColor;
|
|
in highp vec4 ls_fP;
|
|
in mediump vec2 v_coord;
|
|
|
|
out mediump vec4 out_color;
|
|
|
|
uniform highp vec4 u_ClipPlane;
|
|
uniform highp vec4 u_PointPlane;
|
|
uniform mediump float u_RenderingMode;
|
|
|
|
void main(void)
|
|
{
|
|
// Round the quad into a disk.
|
|
if (dot(v_coord, v_coord) > 1.0)
|
|
discard;
|
|
|
|
float onPlane = sign(dot((ls_fP.xyz-u_PointPlane.xyz), u_ClipPlane.xyz));
|
|
if (u_RenderingMode == (onPlane+1)/2) {
|
|
discard;
|
|
}
|
|
|
|
out_color = fColor;
|
|
}
|
|
)DELIM";
|
|
|
|
//------------------------------------------------------------------------------
|
|
// compatibility shaders
|
|
|
|
const char VERTEX_SOURCE_COLOR_COMP[]=R"DELIM(
|
|
varying highp vec3 a_Pos;
|
|
varying highp vec3 a_Normal;
|
|
varying mediump vec3 a_Color;
|
|
|
|
varying highp vec4 vs_fP; // view space position
|
|
varying highp vec3 fN;
|
|
varying mediump vec4 fColor;
|
|
|
|
uniform highp mat4 u_Mvp;
|
|
uniform highp mat4 u_Mv;
|
|
uniform mediump float u_PointSize;
|
|
uniform mediump vec3 u_DefaultColor;
|
|
uniform bool u_UseDefaultColor;
|
|
|
|
void main(void)
|
|
{
|
|
vec4 pos = vec4(a_Pos, 1.0);
|
|
|
|
vs_fP = u_Mv * pos;
|
|
highp mat3 mv_matrix_3;
|
|
mv_matrix_3[0] = mv_matrix[0].xyz;
|
|
mv_matrix_3[1] = mv_matrix[1].xyz;
|
|
mv_matrix_3[2] = mv_matrix[2].xyz;
|
|
fN = mv_matrix_3* a_Normal;
|
|
|
|
fColor = vec4(a_Color, 1.0);
|
|
if (u_UseDefaultColor)
|
|
{
|
|
fColor = vec4(u_DefaultColor, 1.0);
|
|
}
|
|
|
|
gl_PointSize = u_PointSize;
|
|
|
|
gl_Position = u_Mvp * pos;
|
|
}
|
|
)DELIM";
|
|
|
|
const char FRAGMENT_SOURCE_COLOR_COMP[]=R"DELIM(
|
|
varying highp vec4 vs_fP;
|
|
varying highp vec3 fN;
|
|
varying mediump vec4 fColor;
|
|
|
|
uniform highp vec4 u_LightPos;
|
|
uniform mediump vec4 u_LightDiff;
|
|
uniform mediump vec4 u_LightSpec;
|
|
uniform mediump vec4 u_LightAmb;
|
|
uniform mediump float u_SpecPower ;
|
|
|
|
void main(void)
|
|
{
|
|
highp vec3 L = u_LightPos.xyz - vs_fP.xyz;
|
|
highp vec3 V = -vs_fP.xyz;
|
|
|
|
highp vec3 a_Normal = normalize(fN);
|
|
L = normalize(L);
|
|
V = normalize(V);
|
|
|
|
highp vec3 R = reflect(-L, a_Normal);
|
|
highp vec4 diffuse = max(dot(a_Normal,L), 0.0) * u_LightDiff * fColor;
|
|
highp vec4 specular = pow(max(dot(R,V), 0.0), u_SpecPower) * u_LightSpec;
|
|
|
|
gl_FragColor = u_LightAmb*fColor + diffuse;
|
|
}
|
|
)DELIM";
|
|
|
|
const char VERTEX_SOURCE_P_L_COMP[]=R"DELIM(
|
|
varying highp vec3 a_Pos;
|
|
varying mediump vec3 a_Color;
|
|
|
|
varying mediump vec4 fColor;
|
|
|
|
uniform highp mat4 u_Mvp;
|
|
uniform mediump float u_PointSize;
|
|
uniform mediump vec3 u_DefaultColor;
|
|
uniform bool u_UseDefaultColor;
|
|
|
|
void main(void)
|
|
{
|
|
fColor = vec4(a_Color, 1.0);
|
|
if (u_UseDefaultColor)
|
|
{
|
|
fColor = vec4(u_DefaultColor, 1.0);
|
|
}
|
|
|
|
gl_PointSize = u_PointSize;
|
|
gl_Position = u_Mvp * vec4(a_Pos, 1.0);
|
|
}
|
|
)DELIM";
|
|
|
|
const char FRAGMENT_SOURCE_P_L_COMP[]=R"DELIM(
|
|
varying mediump vec4 fColor;
|
|
|
|
void main(void)
|
|
{
|
|
gl_FragColor = fColor;
|
|
}
|
|
)DELIM";
|
|
|
|
/* const char vertex_source_clipping_plane_comp[]=R"DELIM(
|
|
attribute highp vec4 vertex;
|
|
|
|
uniform highp mat4 vp_matrix;
|
|
uniform highp mat4 m_matrix;
|
|
|
|
void main(void)
|
|
{
|
|
gl_Position = vp_matrix * m_matrix * vertex;
|
|
}
|
|
)DELIM";
|
|
|
|
const char fragment_source_clipping_plane_comp[]=R"DELIM(
|
|
out highp vec4 out_color;
|
|
void main(void)
|
|
{
|
|
out_color = vec4(0.0, 0.0, 0.0, 1.0);
|
|
}
|
|
)DELIM";
|
|
*/
|
|
|
|
}
|
|
|
|
#endif // CGAL_BASIC_SHADERS_H
|