rayzee 6.4.0 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -5
- package/dist/rayzee.es.js +4953 -4225
- package/dist/rayzee.es.js.map +1 -1
- package/dist/rayzee.umd.js +157 -236
- package/dist/rayzee.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/EngineDefaults.js +29 -13
- package/src/PathTracerApp.js +119 -26
- package/src/Pipeline/PipelineContext.js +1 -2
- package/src/Pipeline/RenderPipeline.js +1 -1
- package/src/Pipeline/RenderStage.js +1 -1
- package/src/Processor/CameraOptimizer.js +0 -5
- package/src/Processor/GeometryExtractor.js +22 -1
- package/src/Processor/KernelManager.js +277 -0
- package/src/Processor/PackedRayBuffer.js +265 -0
- package/src/Processor/QueueManager.js +173 -0
- package/src/Processor/SceneProcessor.js +1 -0
- package/src/Processor/ShaderBuilder.js +11 -316
- package/src/Processor/StorageTexturePool.js +29 -15
- package/src/Processor/TextureCreator.js +6 -0
- package/src/Processor/VRAMTracker.js +169 -0
- package/src/Processor/utils.js +11 -110
- package/src/RenderSettings.js +1 -3
- package/src/Stages/ASVGF.js +76 -20
- package/src/Stages/BilateralFilter.js +34 -10
- package/src/Stages/EdgeFilter.js +2 -3
- package/src/Stages/MotionVector.js +16 -9
- package/src/Stages/NormalDepth.js +17 -5
- package/src/Stages/PathTracer.js +671 -1456
- package/src/Stages/PathTracerStage.js +1451 -0
- package/src/Stages/SSRC.js +32 -15
- package/src/Stages/Variance.js +35 -12
- package/src/TSL/BVHTraversal.js +7 -1
- package/src/TSL/Common.js +12 -2
- package/src/TSL/CompactKernel.js +110 -0
- package/src/TSL/DebugKernel.js +98 -0
- package/src/TSL/Environment.js +13 -11
- package/src/TSL/ExtendKernel.js +75 -0
- package/src/TSL/FinalWriteKernel.js +121 -0
- package/src/TSL/GenerateKernel.js +109 -0
- package/src/TSL/LightsSampling.js +2 -2
- package/src/TSL/MaterialTransmission.js +32 -2
- package/src/TSL/PathTracerCore.js +43 -912
- package/src/TSL/ShadeKernel.js +873 -0
- package/src/TSL/Struct.js +5 -0
- package/src/TSL/Subsurface.js +232 -0
- package/src/TSL/patches.js +81 -4
- package/src/index.js +3 -0
- package/src/managers/CameraManager.js +1 -1
- package/src/managers/DenoisingManager.js +40 -75
- package/src/managers/EnvironmentManager.js +30 -39
- package/src/managers/MaterialDataManager.js +60 -1
- package/src/managers/OverlayManager.js +7 -22
- package/src/managers/UniformManager.js +1 -3
- package/src/managers/helpers/TileHelper.js +2 -2
- package/src/Stages/AdaptiveSampling.js +0 -483
- package/src/TSL/PathTracer.js +0 -384
- package/src/managers/TileManager.js +0 -298
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FinalWriteKernel.js — wavefront final output: temporal accumulation + MRT StorageTexture writes (16×16, 2D).
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
Fn, wgslFn, float, vec2, vec4, int, uint, uvec2,
|
|
7
|
+
If, mix, select, texture, textureStore,
|
|
8
|
+
localId, workgroupId,
|
|
9
|
+
} from 'three/tsl';
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
readRayRadiance, readGBuffer, gbDecodeNormalDepth, gbDecodeAlbedo,
|
|
13
|
+
} from '../Processor/PackedRayBuffer.js';
|
|
14
|
+
|
|
15
|
+
const WG_SIZE = 16;
|
|
16
|
+
|
|
17
|
+
// Debug mode 11: NaN/Inf detector — red where the accumulated color is NaN/Inf, black elsewhere.
|
|
18
|
+
const nanInfToRed = /*@__PURE__*/ wgslFn( `
|
|
19
|
+
fn nanInfToRed( c: vec3f ) -> vec3f {
|
|
20
|
+
let isNan = c.x != c.x || c.y != c.y || c.z != c.z;
|
|
21
|
+
let isInf = abs( c.x ) > 1e30f || abs( c.y ) > 1e30f || abs( c.z ) > 1e30f;
|
|
22
|
+
if ( isNan || isInf ) { return vec3f( 1.0f, 0.0f, 0.0f ); }
|
|
23
|
+
return vec3f( 0.0f );
|
|
24
|
+
}
|
|
25
|
+
` );
|
|
26
|
+
|
|
27
|
+
export function buildFinalWriteKernel( params ) {
|
|
28
|
+
|
|
29
|
+
const {
|
|
30
|
+
rayBufferRO, gBufferRO,
|
|
31
|
+
writeColorTex, writeNDTex, writeAlbedoTex,
|
|
32
|
+
resolution, frame,
|
|
33
|
+
enableAccumulation, hasPreviousAccumulated, accumulationAlpha, cameraIsMoving,
|
|
34
|
+
transparentBackground,
|
|
35
|
+
prevAccumTexture, prevNormalDepthTexture, prevAlbedoTexture,
|
|
36
|
+
renderWidth, renderHeight,
|
|
37
|
+
// Multi-sample: average S sample-slots per pixel (slot = pixel + k*w*h, w*h from the resolution uniform).
|
|
38
|
+
samplesPerPass = 1,
|
|
39
|
+
visMode,
|
|
40
|
+
} = params;
|
|
41
|
+
|
|
42
|
+
const S = samplesPerPass | 0;
|
|
43
|
+
|
|
44
|
+
const computeFn = Fn( () => {
|
|
45
|
+
|
|
46
|
+
const gx = int( workgroupId.x ).mul( WG_SIZE ).add( int( localId.x ) );
|
|
47
|
+
const gy = int( workgroupId.y ).mul( WG_SIZE ).add( int( localId.y ) );
|
|
48
|
+
|
|
49
|
+
If( gx.lessThan( renderWidth ).and( gy.lessThan( renderHeight ) ), () => {
|
|
50
|
+
|
|
51
|
+
const pixelIndex = gy.mul( int( resolution.x ) ).add( gx );
|
|
52
|
+
const rayID = uint( pixelIndex );
|
|
53
|
+
|
|
54
|
+
// Average the S sub-samples; MRT (normal/depth/albedo) from sub-sample 0.
|
|
55
|
+
const sampleColor = ( () => {
|
|
56
|
+
|
|
57
|
+
if ( S <= 1 ) return readRayRadiance( rayBufferRO, rayID );
|
|
58
|
+
const acc = readRayRadiance( rayBufferRO, rayID ).toVar();
|
|
59
|
+
const mrps = uint( resolution.x ).mul( uint( resolution.y ) ).toVar(); // w*h from the resolution uniform, not baked
|
|
60
|
+
for ( let k = 1; k < S; k ++ ) {
|
|
61
|
+
|
|
62
|
+
acc.addAssign( readRayRadiance( rayBufferRO, rayID.add( uint( k ).mul( mrps ) ) ) );
|
|
63
|
+
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
acc.assign( acc.div( float( S ) ) );
|
|
67
|
+
return acc;
|
|
68
|
+
|
|
69
|
+
} )();
|
|
70
|
+
// MRT comes from the per-pixel G-buffer (rayID == pixelIndex here, i.e. sub-sample 0). Half-packed: decode.
|
|
71
|
+
const gbuf = readGBuffer( gBufferRO, rayID );
|
|
72
|
+
const normalDepth = gbDecodeNormalDepth( gbuf );
|
|
73
|
+
const albedoID = vec4( gbDecodeAlbedo( gbuf ), 0.0 );
|
|
74
|
+
|
|
75
|
+
const finalColor = sampleColor.xyz.toVar();
|
|
76
|
+
const finalNormalDepth = normalDepth.toVar();
|
|
77
|
+
const finalAlbedo = albedoID.xyz.toVar();
|
|
78
|
+
const outputAlpha = select( transparentBackground, sampleColor.w, float( 1.0 ) ).toVar();
|
|
79
|
+
|
|
80
|
+
const pixelCoord = vec2( float( gx ).add( 0.5 ), float( gy ).add( 0.5 ) );
|
|
81
|
+
const prevUV = pixelCoord.div( resolution );
|
|
82
|
+
|
|
83
|
+
// visMode 11 (NaN/Inf) bypasses accumulation (megakernel parity main_TSL_PathTracer.js:355) so the
|
|
84
|
+
// detector runs on each frame's fresh color — else mix() propagates a transient NaN and it stays red forever.
|
|
85
|
+
If( enableAccumulation.and( cameraIsMoving.not() ).and( frame.greaterThan( uint( 0 ) ) ).and( hasPreviousAccumulated ).and( visMode.notEqual( int( 11 ) ) ), () => {
|
|
86
|
+
|
|
87
|
+
const prevAccumSample = texture( prevAccumTexture, prevUV, 0 ).toVar();
|
|
88
|
+
|
|
89
|
+
finalColor.assign( mix( prevAccumSample.xyz, sampleColor.xyz, accumulationAlpha ) );
|
|
90
|
+
finalNormalDepth.assign( mix( texture( prevNormalDepthTexture, prevUV, 0 ), finalNormalDepth, accumulationAlpha ) );
|
|
91
|
+
finalAlbedo.assign( mix( texture( prevAlbedoTexture, prevUV, 0 ).xyz, finalAlbedo, accumulationAlpha ) );
|
|
92
|
+
|
|
93
|
+
If( transparentBackground, () => {
|
|
94
|
+
|
|
95
|
+
outputAlpha.assign( mix( prevAccumSample.w, sampleColor.w, accumulationAlpha ) );
|
|
96
|
+
|
|
97
|
+
} );
|
|
98
|
+
|
|
99
|
+
} );
|
|
100
|
+
|
|
101
|
+
// Debug mode 11: flag NaN/Inf on the accumulated color (red on NaN/Inf, black elsewhere).
|
|
102
|
+
If( visMode.equal( int( 11 ) ), () => {
|
|
103
|
+
|
|
104
|
+
finalColor.assign( nanInfToRed( finalColor ) );
|
|
105
|
+
|
|
106
|
+
} );
|
|
107
|
+
|
|
108
|
+
const uintCoord = uvec2( uint( gx ), uint( gy ) );
|
|
109
|
+
textureStore( writeColorTex, uintCoord, vec4( finalColor, outputAlpha ) ).toWriteOnly();
|
|
110
|
+
textureStore( writeNDTex, uintCoord, finalNormalDepth ).toWriteOnly();
|
|
111
|
+
textureStore( writeAlbedoTex, uintCoord, vec4( finalAlbedo, 1.0 ) ).toWriteOnly();
|
|
112
|
+
|
|
113
|
+
} );
|
|
114
|
+
|
|
115
|
+
} );
|
|
116
|
+
|
|
117
|
+
return computeFn;
|
|
118
|
+
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export { WG_SIZE as FINALWRITE_WG_SIZE };
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GenerateKernel.js — wavefront primary ray generation (16×16, 2D screen-space dispatch).
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
Fn, float, vec2, vec3, vec4, int, uint,
|
|
7
|
+
If, select,
|
|
8
|
+
localId, workgroupId,
|
|
9
|
+
} from 'three/tsl';
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
getDecorrelatedSeed,
|
|
13
|
+
pcgHash,
|
|
14
|
+
getStratifiedSample,
|
|
15
|
+
} from './Random.js';
|
|
16
|
+
|
|
17
|
+
import { generateRayFromCamera } from './BVHTraversal.js';
|
|
18
|
+
import { Ray } from './Struct.js';
|
|
19
|
+
import { RAY_FLAG } from '../Processor/QueueManager.js';
|
|
20
|
+
import {
|
|
21
|
+
writeRayOriginMeta, writeRayDirFlags, writeRayThroughputPdf,
|
|
22
|
+
writeRayRadiance, writeGBuffer,
|
|
23
|
+
writeMediumStack,
|
|
24
|
+
} from '../Processor/PackedRayBuffer.js';
|
|
25
|
+
|
|
26
|
+
const WG_SIZE = 16;
|
|
27
|
+
|
|
28
|
+
export function buildGenerateKernel( params ) {
|
|
29
|
+
|
|
30
|
+
const {
|
|
31
|
+
rayBufferRW, rngBufferRW, gBufferRW,
|
|
32
|
+
resolution, frame,
|
|
33
|
+
cameraWorldMatrix, cameraProjectionMatrixInverse,
|
|
34
|
+
enableDOF, focalLength, aperture, focusDistance, sceneScale, apertureScale, anamorphicRatio,
|
|
35
|
+
renderWidth, renderHeight,
|
|
36
|
+
// Multi-sample: S primary rays/pixel/frame; S>1 dispatch covers h*S rows, ray lands in slot subSample*(w*h) + pixelIndex.
|
|
37
|
+
samplesPerPass = 1,
|
|
38
|
+
transmissiveBounces, // per-ray refraction budget (megakernel parity: PathTracerCore.js:606)
|
|
39
|
+
transparentBackground, // alpha inits to 1 here (megakernel parity: PathTracerCore.js:554) — env-escape-without-opaque zeroes it in Shade
|
|
40
|
+
} = params;
|
|
41
|
+
|
|
42
|
+
const S = samplesPerPass | 0;
|
|
43
|
+
|
|
44
|
+
const computeFn = Fn( () => {
|
|
45
|
+
|
|
46
|
+
const gx = int( workgroupId.x ).mul( WG_SIZE ).add( int( localId.x ) );
|
|
47
|
+
const gyRaw = int( workgroupId.y ).mul( WG_SIZE ).add( int( localId.y ) );
|
|
48
|
+
|
|
49
|
+
const subSample = S > 1 ? gyRaw.div( renderHeight ).toVar() : int( 0 );
|
|
50
|
+
const gy = S > 1 ? gyRaw.sub( subSample.mul( renderHeight ) ).toVar() : gyRaw;
|
|
51
|
+
const yBound = S > 1 ? renderHeight.mul( int( S ) ) : renderHeight;
|
|
52
|
+
|
|
53
|
+
If( gx.lessThan( renderWidth ).and( gyRaw.lessThan( yBound ) ), () => {
|
|
54
|
+
|
|
55
|
+
const pixelCoord = vec2( float( gx ).add( 0.5 ), float( gy ).add( 0.5 ) );
|
|
56
|
+
const pixelIndex = gy.mul( int( resolution.x ) ).add( gx );
|
|
57
|
+
// maxRaysPerSample = w*h, derived from the resolution uniform (NOT baked) so resize never changes the WGSL.
|
|
58
|
+
const rayID = S > 1
|
|
59
|
+
? uint( pixelIndex ).add( uint( subSample ).mul( uint( resolution.x ).mul( uint( resolution.y ) ) ) )
|
|
60
|
+
: uint( pixelIndex );
|
|
61
|
+
|
|
62
|
+
const screenPosition = pixelCoord.div( resolution ).mul( 2.0 ).sub( 1.0 ).toVar();
|
|
63
|
+
screenPosition.y.assign( screenPosition.y.negate() );
|
|
64
|
+
|
|
65
|
+
const baseSeed = getDecorrelatedSeed( { pixelCoord, rayIndex: subSample, frame } ).toVar();
|
|
66
|
+
const seed = pcgHash( { state: baseSeed } ).toVar();
|
|
67
|
+
|
|
68
|
+
const stratifiedJitter = getStratifiedSample( pixelCoord, subSample, int( S ), seed, resolution, frame ).toVar();
|
|
69
|
+
|
|
70
|
+
const jitterScale = vec2( 2.0 ).div( resolution );
|
|
71
|
+
const jitter = stratifiedJitter.sub( 0.5 ).mul( jitterScale );
|
|
72
|
+
const jitteredScreenPosition = screenPosition.add( jitter );
|
|
73
|
+
|
|
74
|
+
const ray = Ray.wrap( generateRayFromCamera(
|
|
75
|
+
jitteredScreenPosition, seed,
|
|
76
|
+
cameraWorldMatrix, cameraProjectionMatrixInverse,
|
|
77
|
+
enableDOF, focalLength, aperture, focusDistance, sceneScale, apertureScale, anamorphicRatio,
|
|
78
|
+
) );
|
|
79
|
+
|
|
80
|
+
writeRayOriginMeta( rayBufferRW, rayID, ray.origin, int( 0 ), int( 0 ) );
|
|
81
|
+
writeRayDirFlags( rayBufferRW, rayID, ray.direction, uint( RAY_FLAG.ACTIVE ) );
|
|
82
|
+
// pdf inits to 0 = prevBouncePdf (megakernel parity PathTracerCore.js:556). The bounce>0 env/emissive
|
|
83
|
+
// MIS gate skips until an opaque scatter writes a real combinedPdf; free bounces preserve it.
|
|
84
|
+
writeRayThroughputPdf( rayBufferRW, rayID, vec4( 1.0, 1.0, 1.0, 0.0 ).xyz, float( 0.0 ) );
|
|
85
|
+
// Alpha inits to 1 in transparent-bg mode (megakernel parity: PathTracerCore.js:554). Shade zeroes
|
|
86
|
+
// it only on env-escape-without-opaque; a ray that dies inside geometry (e.g. SSS walk termination)
|
|
87
|
+
// keeps alpha 1 → solid. Non-transparent mode is inert (FinalWrite forces alpha 1).
|
|
88
|
+
writeRayRadiance( rayBufferRW, rayID, vec4( vec3( 0.0 ), select( transparentBackground, float( 1.0 ), float( 0.0 ) ) ) );
|
|
89
|
+
|
|
90
|
+
If( subSample.equal( int( 0 ) ), () => {
|
|
91
|
+
|
|
92
|
+
// default: normal +Z, depth 1 (far), black albedo (background/miss)
|
|
93
|
+
writeGBuffer( gBufferRW, uint( pixelIndex ), vec3( 0.0, 0.0, 1.0 ), float( 1.0 ), vec3( 0.0 ) );
|
|
94
|
+
|
|
95
|
+
} );
|
|
96
|
+
|
|
97
|
+
writeMediumStack( rayBufferRW, rayID, uint( 0 ), uint( transmissiveBounces ), float( 1.0 ), float( 1.0 ), float( 1.0 ) );
|
|
98
|
+
|
|
99
|
+
rngBufferRW.element( rayID ).assign( seed );
|
|
100
|
+
|
|
101
|
+
} );
|
|
102
|
+
|
|
103
|
+
} );
|
|
104
|
+
|
|
105
|
+
return computeFn;
|
|
106
|
+
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export { WG_SIZE as GENERATE_WG_SIZE };
|
|
@@ -704,7 +704,7 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
704
704
|
materialBuffer,
|
|
705
705
|
// Environment resources
|
|
706
706
|
envTexture, environmentIntensity, envMatrix,
|
|
707
|
-
|
|
707
|
+
envCDFTexture,
|
|
708
708
|
envTotalSum, envCompensationDelta, envResolution,
|
|
709
709
|
enableEnvironmentLight,
|
|
710
710
|
] ) => {
|
|
@@ -969,7 +969,7 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
969
969
|
|
|
970
970
|
// Sample direction + PDF + color from importance-sampled environment
|
|
971
971
|
const envSampleResult = sampleEquirectProbability(
|
|
972
|
-
envTexture,
|
|
972
|
+
envTexture, envCDFTexture,
|
|
973
973
|
envMatrix, environmentIntensity, envTotalSum, envCompensationDelta, envResolution, envRandom, envColor
|
|
974
974
|
).toVar();
|
|
975
975
|
|
|
@@ -27,6 +27,7 @@ import { iorToFresnel0, fresnelSchlickFloat } from './Fresnel.js';
|
|
|
27
27
|
import { DistributionGGX } from './MaterialProperties.js';
|
|
28
28
|
import { ImportanceSampleGGX } from './MaterialSampling.js';
|
|
29
29
|
import { RandomValue, pcgHash } from './Random.js';
|
|
30
|
+
import { handleSubsurfaceEntry, SubsurfaceEntryResult } from './Subsurface.js';
|
|
30
31
|
|
|
31
32
|
// ================================================================================
|
|
32
33
|
// STRUCTS (local to transmission)
|
|
@@ -43,6 +44,7 @@ export const MaterialInteractionResult = struct( {
|
|
|
43
44
|
continueRay: 'bool', // Whether the ray should continue without further BRDF evaluation
|
|
44
45
|
isTransmissive: 'bool', // Flag to indicate this was a transmissive interaction
|
|
45
46
|
isAlphaSkip: 'bool', // Flag to indicate this was an alpha skip
|
|
47
|
+
isSubsurface: 'bool', // Flag to indicate this entered/exited a subsurface medium
|
|
46
48
|
didReflect: 'bool', // Whether TIR/reflection occurred (for medium stack update)
|
|
47
49
|
entering: 'bool', // Whether ray is entering or exiting medium
|
|
48
50
|
direction: 'vec3', // New ray direction if continuing
|
|
@@ -493,6 +495,7 @@ export const handleMaterialTransparency = Fn( ( [
|
|
|
493
495
|
continueRay: false,
|
|
494
496
|
isTransmissive: false,
|
|
495
497
|
isAlphaSkip: false,
|
|
498
|
+
isSubsurface: false,
|
|
496
499
|
didReflect: false,
|
|
497
500
|
entering: false,
|
|
498
501
|
direction: ray.direction,
|
|
@@ -501,8 +504,9 @@ export const handleMaterialTransparency = Fn( ( [
|
|
|
501
504
|
pathWavelength: pathWavelength,
|
|
502
505
|
} ).toVar();
|
|
503
506
|
|
|
504
|
-
// Fast path for fully opaque materials (most common case)
|
|
505
|
-
|
|
507
|
+
// Fast path for fully opaque, non-scattering materials (most common case).
|
|
508
|
+
// Subsurface materials are opaque (transmission==0) but must NOT take this path.
|
|
509
|
+
If( material.alphaMode.equal( int( 0 ) ).and( material.transmission.lessThanEqual( 0.0 ) ).and( material.subsurface.lessThanEqual( 0.0 ) ), () => {
|
|
506
510
|
|
|
507
511
|
// no interaction needed
|
|
508
512
|
|
|
@@ -584,6 +588,32 @@ export const handleMaterialTransparency = Fn( ( [
|
|
|
584
588
|
|
|
585
589
|
} );
|
|
586
590
|
|
|
591
|
+
// Subsurface (independent of transmission; works at transmission==0). Entry is a lottery
|
|
592
|
+
// (prob = weight) so 1-weight falls through to the opaque BRDF; exit is deterministic.
|
|
593
|
+
If( handled.not().and( material.subsurface.greaterThan( 0.0 ) ), () => {
|
|
594
|
+
|
|
595
|
+
const entering = dot( ray.direction, normal ).lessThan( 0.0 );
|
|
596
|
+
const doEnter = entering.not().or( RandomValue( rngState ).lessThan( material.subsurface ) );
|
|
597
|
+
|
|
598
|
+
If( doEnter, () => {
|
|
599
|
+
|
|
600
|
+
const ssResult = SubsurfaceEntryResult.wrap( handleSubsurfaceEntry(
|
|
601
|
+
ray.direction, normal, material, entering, rngState,
|
|
602
|
+
currentMediumIOR, previousMediumIOR,
|
|
603
|
+
) ).toVar();
|
|
604
|
+
|
|
605
|
+
result.direction.assign( ssResult.direction );
|
|
606
|
+
result.throughput.assign( ssResult.throughput );
|
|
607
|
+
result.continueRay.assign( true );
|
|
608
|
+
result.isSubsurface.assign( true );
|
|
609
|
+
result.didReflect.assign( ssResult.didReflect );
|
|
610
|
+
result.entering.assign( entering );
|
|
611
|
+
result.alpha.assign( 1.0 );
|
|
612
|
+
|
|
613
|
+
} );
|
|
614
|
+
|
|
615
|
+
} );
|
|
616
|
+
|
|
587
617
|
} );
|
|
588
618
|
|
|
589
619
|
return result;
|