Skip to content

Commit

Permalink
feat(light,volumeMapper): add positional light and multiple light sup…
Browse files Browse the repository at this point in the history
…port
  • Loading branch information
JiayiXuu committed May 18, 2022
1 parent 94c1ed2 commit c84b5a0
Show file tree
Hide file tree
Showing 5 changed files with 283 additions and 105 deletions.
29 changes: 25 additions & 4 deletions Sources/Rendering/Core/Camera/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ function vtkCamera(publicAPI, model) {
const dopbasis = new Float64Array([0.0, 0.0, -1.0]);
const upbasis = new Float64Array([0.0, 1.0, 0.0]);
const tmpMatrix = mat4.identity(new Float64Array(16));
const tmpMatrix2 = mat4.identity(new Float64Array(16));
const tmpvec1 = new Float64Array(3);
const tmpvec2 = new Float64Array(3);
const tmpvec3 = new Float64Array(3);
Expand Down Expand Up @@ -71,7 +72,7 @@ function vtkCamera(publicAPI, model) {

// recompute the focal distance
publicAPI.computeDistance();

publicAPI.computeCameraLightTransform();
publicAPI.modified();
};

Expand All @@ -90,7 +91,7 @@ function vtkCamera(publicAPI, model) {

// recompute the focal distance
publicAPI.computeDistance();

publicAPI.computeCameraLightTransform();
publicAPI.modified();
};

Expand All @@ -113,7 +114,7 @@ function vtkCamera(publicAPI, model) {
model.focalPoint[0] = model.position[0] + vec[0] * model.distance;
model.focalPoint[1] = model.position[1] + vec[1] * model.distance;
model.focalPoint[2] = model.position[2] + vec[2] * model.distance;

publicAPI.computeCameraLightTransform();
publicAPI.modified();
};

Expand Down Expand Up @@ -348,7 +349,26 @@ function vtkCamera(publicAPI, model) {
publicAPI.getFrustumPlanes = (aspect) => {
// Return array of 24 params (4 params for each of 6 plane equations)
};
publicAPI.getCameraLightTransformMatrix = () => {};
publicAPI.getCameraLightTransformMatrix = (matrix) => {
mat4.copy(matrix, model.cameraLightTransform);
return matrix;
};

publicAPI.computeCameraLightTransform = () => {
// not sure if this is the correct transformation, based on the same funciton in VTK
mat4.copy(tmpMatrix, publicAPI.getViewMatrix());
mat4.invert(tmpMatrix, tmpMatrix);

mat4.fromScaling(tmpMatrix2, [
model.distance,
model.distance,
model.distance,
]);
mat4.multiply(tmpMatrix, tmpMatrix, tmpMatrix2);
mat4.identity(model.cameraLightTransform);
mat4.translate(model.cameraLightTransform, tmpMatrix, [0.0, 0.0, -1.0]);
};

publicAPI.deepCopy = (sourceCamera) => {};

publicAPI.physicalOrientationToWorldDirection = (ori) => {
Expand Down Expand Up @@ -715,6 +735,7 @@ export const DEFAULT_VALUES = {
freezeFocalPoint: false,
projectionMatrix: null,
viewMatrix: null,
cameraLightTransform: mat4.create(),

// used for world to physical transformations
physicalTranslation: [0, 0, 0],
Expand Down
20 changes: 16 additions & 4 deletions Sources/Rendering/Core/Light/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import macro from 'vtk.js/Sources/macros';
import * as vtkMath from 'vtk.js/Sources/Common/Core/Math';
import { vec3 } from 'gl-matrix';

// ----------------------------------------------------------------------------

Expand All @@ -12,19 +13,29 @@ export const LIGHT_TYPES = ['HeadLight', 'CameraLight', 'SceneLight'];
function vtkLight(publicAPI, model) {
// Set our className
model.classHierarchy.push('vtkLight');
const tmpVec = new Float64Array(3);

publicAPI.getTransformedPosition = () => {
if (model.transformMatrix) {
return []; // FIXME !!!!
vec3.transformMat4(tmpVec, model.position, model.transformMatrix);
} else {
vec3.set(tmpVec, model.position[0], model.position[1], model.position[2]);
}
return [].concat(model.position);
return tmpVec;
};

publicAPI.getTransformedFocalPoint = () => {
if (model.transformMatrix) {
return []; // FIXME !!!!
vec3.transformMat4(tmpVec, model.focalPoint, model.transformMatrix);
} else {
vec3.set(
tmpVec,
model.focalPoint[0],
model.focalPoint[1],
model.focalPoint[2]
);
}
return [].concat(model.focalPoint);
return tmpVec;
};

publicAPI.getDirection = () => {
Expand Down Expand Up @@ -109,6 +120,7 @@ export function extend(publicAPI, model, initialValues = {}) {
'transformMatrix',
'lightType',
'shadowAttenuation',
'attenuationValues',
]);
macro.setGetArray(
publicAPI,
Expand Down
6 changes: 5 additions & 1 deletion Sources/Rendering/Core/Renderer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ function vtkRenderer(publicAPI, model) {
const camera = publicAPI.getActiveCameraAndResetIfCreated();

model.lights.forEach((light) => {
if (light.lightTypeIsSceneLight() || light.lightTypeIsCameraLight()) {
if (light.lightTypeIsSceneLight()) {
// Do nothing. Don't reset the transform matrix because applications
// may have set a custom matrix. Only reset the transform matrix in
// vtkLight::SetLightTypeToSceneLight()
Expand All @@ -65,6 +65,10 @@ function vtkRenderer(publicAPI, model) {
light.setPositionFrom(camera.getPositionByReference());
light.setFocalPointFrom(camera.getFocalPointByReference());
light.modified(camera.getMTime());
} else if (light.lightTypeIsCameraLight()) {
light.setTransformMatrix(
camera.getCameraLightTransformMatrix(mat4.create())
);
} else {
vtkErrorMacro('light has unknown light type', light.get());
}
Expand Down
193 changes: 104 additions & 89 deletions Sources/Rendering/OpenGL/VolumeMapper/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,58 +237,50 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
};

publicAPI.replaceShaderLight = (shaders, ren, actor) => {
if (model.lastLightComplexity === 0) {
return;
}
let FSSource = shaders.Fragment;
// check for shadow maps - not implemented yet, skip
// const shadowFactor = '';

// check for shadow maps
const shadowFactor = '';

switch (model.lastLightComplexity) {
case 1: // headlight
case 2: // light kit
case 3: {
// positional not implemented fallback to directional
let lightNum = 0;
ren.getLights().forEach((light) => {
const status = light.getSwitch();
if (status > 0) {
FSSource = vtkShaderProgram.substitute(
FSSource,
'//VTK::Light::Dec',
[
// intensity weighted color
`uniform vec3 lightColor${lightNum};`,
`uniform vec3 lightDirectionVC${lightNum}; // normalized`,
`uniform vec3 lightHalfAngleVC${lightNum}; // normalized`,
'//VTK::Light::Dec',
],
false
).result;
FSSource = vtkShaderProgram.substitute(
FSSource,
'//VTK::Light::Impl',
[
// ` float df = max(0.0, dot(normal.rgb, -lightDirectionVC${lightNum}));`,
` float df = abs(dot(normal.rgb, -lightDirectionVC${lightNum}));`,
` diffuse += ((df${shadowFactor}) * lightColor${lightNum});`,
// ' if (df > 0.0)',
// ' {',
// ` float sf = pow( max(0.0, dot(lightHalfAngleWC${lightNum},normal.rgb)), specularPower);`,
` float sf = pow( abs(dot(lightHalfAngleVC${lightNum},normal.rgb)), vSpecularPower);`,
` specular += ((sf${shadowFactor}) * lightColor${lightNum});`,
// ' }',
' //VTK::Light::Impl',
],
false
).result;
lightNum++;
}
});
break;
// to-do: single out the case when complexity = 1

// only account for lights that are switched on
let lightNum = 0;
ren.getLights().forEach((light) => {
if (light.getSwitch()) {
lightNum += 1;
}
case 0: // no lighting, tcolor is fine as is
default:
});
FSSource = vtkShaderProgram.substitute(
FSSource,
'//VTK::Light::Dec',
[
`uniform int lightNum;`,
`uniform bool twoSidedLighting;`,
`uniform vec3 lightColor[${lightNum}];`,
`uniform vec3 lightDirectionVC[${lightNum}]; // normalized`,
`uniform vec3 lightHalfAngleVC[${lightNum}];`,
'//VTK::Light::Dec',
],
false
).result;
// support any number of lights
if (model.lastLightComplexity === 3) {
FSSource = vtkShaderProgram.substitute(
FSSource,
'//VTK::Light::Dec',
[
`uniform vec3 lightPositionVC[${lightNum}];`,
`uniform vec3 lightAttenuation[${lightNum}];`,
`uniform float lightConeAngle[${lightNum}];`,
`uniform float lightExponent[${lightNum}];`,
`uniform int lightPositional[${lightNum}];`,
],
false
).result;
}

shaders.Fragment = FSSource;
};

Expand Down Expand Up @@ -763,48 +755,71 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
program.setUniformMatrix('PCVCMatrix', model.projectionToView);

// handle lighting values
switch (model.lastLightComplexity) {
case 1: // headlight
case 2: // light kit
case 3: {
// positional not implemented fallback to directional
// mat3.transpose(keyMats.normalMatrix, keyMats.normalMatrix);
let lightNum = 0;
const lightColor = [];
ren.getLights().forEach((light) => {
const status = light.getSwitch();
if (status > 0) {
const dColor = light.getColor();
const intensity = light.getIntensity();
lightColor[0] = dColor[0] * intensity;
lightColor[1] = dColor[1] * intensity;
lightColor[2] = dColor[2] * intensity;
program.setUniform3fArray(`lightColor${lightNum}`, lightColor);
const ldir = light.getDirection();
vec3.set(normal, ldir[0], ldir[1], ldir[2]);
vec3.transformMat3(normal, normal, keyMats.normalMatrix);
program.setUniform3f(
`lightDirectionVC${lightNum}`,
normal[0],
normal[1],
normal[2]
);
// camera DOP is 0,0,-1.0 in VC
const halfAngle = [
-0.5 * normal[0],
-0.5 * normal[1],
-0.5 * (normal[2] - 1.0),
];
program.setUniform3fArray(`lightHalfAngleVC${lightNum}`, halfAngle);
lightNum++;
}
});
// mat3.transpose(keyMats.normalMatrix, keyMats.normalMatrix);
break;
if (model.lastLightComplexity === 0) {
return;
}
let lightNum = 0;
const lightColor = [];
const lightDir = [];
const halfAngle = [];
ren.getLights().forEach((light) => {
const status = light.getSwitch();
if (status > 0) {
const dColor = light.getColor();
const intensity = light.getIntensity();
lightColor[0 + lightNum * 3] = dColor[0] * intensity;
lightColor[1 + lightNum * 3] = dColor[1] * intensity;
lightColor[2 + lightNum * 3] = dColor[2] * intensity;
const ldir = light.getDirection();
vec3.set(normal, ldir[0], ldir[1], ldir[2]);
vec3.transformMat3(normal, normal, keyMats.normalMatrix); // in view coordinat
vec3.normalize(normal, normal);
lightDir[0 + lightNum * 3] = normal[0];
lightDir[1 + lightNum * 3] = normal[1];
lightDir[2 + lightNum * 3] = normal[2];
// camera DOP is 0,0,-1.0 in VC
halfAngle[0 + lightNum * 3] = -0.5 * normal[0];
halfAngle[1 + lightNum * 3] = -0.5 * normal[1];
halfAngle[2 + lightNum * 3] = -0.5 * (normal[2] - 1.0);
lightNum++;
}
case 0: // no lighting, tcolor is fine as is
default:
break;
});
program.setUniformi('twoSidedLighting', ren.getTwoSidedLighting());
program.setUniformi('lightNum', lightNum);
program.setUniform3fv('lightColor', lightColor);
program.setUniform3fv('lightDirectionVC', lightDir);
program.setUniform3fv('lightHalfAngleVC', halfAngle);

if (model.lastLightComplexity === 3) {
lightNum = 0;
const lightPositionVC = [];
const lightAttenuation = [];
const lightConeAngle = [];
const lightExponent = [];
const lightPositional = [];
ren.getLights().forEach((light) => {
const status = light.getSwitch();
if (status > 0) {
const attenuation = light.getAttenuationValues();
lightAttenuation[0 + lightNum * 3] = attenuation[0];
lightAttenuation[1 + lightNum * 3] = attenuation[1];
lightAttenuation[2 + lightNum * 3] = attenuation[2];
lightExponent[lightNum] = light.getExponent();
lightConeAngle[lightNum] = light.getConeAngle();
lightPositional[lightNum] = light.getPositional();
const lp = light.getTransformedPosition();
vec3.transformMat4(lp, lp, model.modelToView);
lightPositionVC[0 + lightNum * 3] = lp[0];
lightPositionVC[1 + lightNum * 3] = lp[1];
lightPositionVC[2 + lightNum * 3] = lp[2];
lightNum += 1;
}
});
program.setUniform3fv('lightPositionVC', lightPositionVC);
program.setUniform3fv('lightAttenuation', lightAttenuation);
program.setUniformfv('lightConeAngle', lightConeAngle);
program.setUniformfv('lightExponent', lightExponent);
program.setUniformiv('lightPositional', lightPositional);
}
};

Expand Down
Loading

0 comments on commit c84b5a0

Please sign in to comment.