[ui] Viewer3D: introduce SphericalHarmonics shader + display mode

Add experimental render mode to display model normals or shading based on spherical harmonics coefficients
This commit is contained in:
Yann Lanthony 2019-02-08 16:31:16 +01:00 committed by Fabien Castan
parent 9aa059a565
commit 3ae95869bd
7 changed files with 194 additions and 1 deletions

View file

@ -3,6 +3,7 @@ import Qt3D.Render 2.9
import Qt3D.Input 2.0
import Qt3D.Extras 2.10
import QtQuick 2.0
import Utils 1.0
import "Materials"
/**
@ -67,6 +68,10 @@ Entity {
// "textured" material resolution order: diffuse map > vertex color data > no color info
material: diffuseMap ? textured : (Scene3DHelper.vertexColorCount(root.parent) ? colored : solid)
}
},
State {
name: "Spherical Harmonics"
PropertyChanges { target: m; material: shMaterial }
}
]
}
@ -112,4 +117,11 @@ Entity {
specular: root.specular
}
SphericalHarmonicsMaterial {
id: shMaterial
objectName: "SphericalHarmonicsMaterial"
effect: SphericalHarmonicsEffect {}
shlSource: Filepath.stringToUrl(Viewer3DSettings.shlFile)
displayNormals: Viewer3DSettings.displayNormals
}
}

View file

@ -0,0 +1,36 @@
import Qt3D.Core 2.0
import Qt3D.Render 2.0
Effect {
id: root
parameters: [
Parameter { name: "shCoeffs[0]"; value: [] },
Parameter { name: "displayNormals"; value: false }
]
techniques: [
Technique {
graphicsApiFilter {
api: GraphicsApiFilter.OpenGL
profile: GraphicsApiFilter.CoreProfile
majorVersion: 3
minorVersion: 1
}
filterKeys: [ FilterKey { name: "renderingStyle"; value: "forward" } ]
renderPasses: [
RenderPass {
shaderProgram: ShaderProgram {
vertexShaderCode: loadSource(Qt.resolvedUrl("shaders/SphericalHarmonics.vert"))
fragmentShaderCode: loadSource(Qt.resolvedUrl("shaders/SphericalHarmonics.frag"))
}
}
]
}
]
}

View file

@ -0,0 +1,63 @@
import Qt3D.Core 2.0
import Qt3D.Render 2.0
import Utils 1.0
Material {
id: root
/// Source file containing coefficients
property url shlSource
/// Spherical Harmonics coefficients (array of 9 vector3d)
property var coefficients: noCoeffs
/// Whether to display normals instead of SH
property bool displayNormals: false
// default coefficients (uniform magenta)
readonly property var noCoeffs: [
Qt.vector3d(0.0, 0.0, 0.0),
Qt.vector3d(0.0, 0.0, 0.0),
Qt.vector3d(0.0, 0.0, 0.0),
Qt.vector3d(1.0, 0.0, 1.0),
Qt.vector3d(0.0, 0.0, 0.0),
Qt.vector3d(0.0, 0.0, 0.0),
Qt.vector3d(0.0, 0.0, 0.0),
Qt.vector3d(0.0, 0.0, 0.0),
Qt.vector3d(0.0, 0.0, 0.0)
]
effect: SphericalHarmonicsEffect {}
onShlSourceChanged: {
if(!shlSource) {
coefficients = noCoeffs;
return;
}
Request.get(Filepath.urlToString(shlSource), function(xhr) {
if(xhr.readyState === XMLHttpRequest.DONE) {
var coeffs = [];
var lines = xhr.responseText.split("\n");
lines.forEach(function(l){
var lineCoeffs = [];
l.split(" ").forEach(function(v){
if(v) { lineCoeffs.push(v); }
})
if(lineCoeffs.length == 3)
coeffs.push(Qt.vector3d(lineCoeffs[0], lineCoeffs[1], lineCoeffs[2]));
});
if(coeffs.length == 9) {
coefficients = coeffs;
}
else {
console.warn("Invalid SHL file: " + shlSource + " with " + coeffs.length + " coefficients.");
coefficients = noCoeffs;
}
}
})
}
parameters: [
Parameter { name: "shCoeffs[0]"; value: coefficients },
Parameter { name: "displayNormals"; value: displayNormals }
]
}

View file

@ -0,0 +1,34 @@
#version 330 core
in vec3 normal;
out vec4 fragColor;
uniform vec3 shCoeffs[9];
uniform bool displayNormals = false;
vec3 resolveSH_Opt(vec3 premulCoefficients[9], vec3 dir)
{
vec3 result = premulCoefficients[0] * dir.x;
result += premulCoefficients[1] * dir.y;
result += premulCoefficients[2] * dir.z;
result += premulCoefficients[3];
vec3 dirSq = dir * dir;
result += premulCoefficients[4] * (dir.x * dir.y);
result += premulCoefficients[5] * (dir.x * dir.z);
result += premulCoefficients[6] * (dir.y * dir.z);
result += premulCoefficients[7] * (dirSq.x - dirSq.y);
result += premulCoefficients[8] * (3 * dirSq.z - 1);
return result;
}
void main()
{
if(displayNormals) {
// Display normals mode
fragColor = vec4(normal, 1.0);
}
else {
// Calculate the color from spherical harmonics coeffs
fragColor = vec4(resolveSH_Opt(shCoeffs, normal), 1.0);
}
}

View file

@ -0,0 +1,16 @@
#version 330 core
in vec3 vertexPosition;
in vec3 vertexNormal;
out vec3 normal;
uniform mat4 modelView;
uniform mat3 modelViewNormal;
uniform mat4 mvp;
void main()
{
normal = vertexNormal;
gl_Position = mvp * vec4( vertexPosition, 1.0 );
}

View file

@ -77,7 +77,7 @@ FocusScope {
if (event.key == Qt.Key_F) {
resetCameraPosition();
}
else if(Qt.Key_1 <= event.key && event.key <= Qt.Key_3)
else if(Qt.Key_1 <= event.key && event.key < Qt.Key_1 + Viewer3DSettings.renderModes.length)
{
Viewer3DSettings.renderMode = event.key - Qt.Key_1;
}
@ -274,8 +274,34 @@ FocusScope {
}
}
FloatingPane {
visible: Viewer3DSettings.renderMode == 3
anchors.bottom: renderModesPanel.top
GridLayout {
columns: 2
rowSpacing: 0
RadioButton { text: "SHL File"; autoExclusive: true; checked: true }
TextField {
text: Viewer3DSettings.shlFile
selectByMouse: true
Layout.minimumWidth: 300
onEditingFinished: Viewer3DSettings.shlFile = text
}
RadioButton {
Layout.columnSpan: 2
autoExclusive: true
text: "Normals"
onCheckedChanged: Viewer3DSettings.displayNormals = checked
}
}
}
// Rendering modes
FloatingPane {
id: renderModesPanel
anchors.bottom: parent.bottom
padding: 4
Row {

View file

@ -26,10 +26,16 @@ Item {
{"name": "Solid", "icon": MaterialIcons.crop_din },
{"name": "Wireframe", "icon": MaterialIcons.details },
{"name": "Textured", "icon": MaterialIcons.texture },
{"name": "Spherical Harmonics", "icon": MaterialIcons.brightness_7}
]
// Current render mode
property int renderMode: 2
// Spherical Harmonics file
property string shlFile: ""
// Whether to display normals
property bool displayNormals: false
// Rasterized point size
property real pointSize: 1.5
// Whether point size is fixed or view dependent