Skip to content

Commit ec06df3

Browse files
author
Thayer J Andrews
committed
CCEffects - Fixes for environment mapping effects and CCRenderTexture
The previous method of finding the transform from an effect to its environment via the root of the scene did not work when the node and environment were children of a CCRenderTexture or when they were being manually rendered to a CCRendertTexture (with direct calls to begin / visit / end). Instead find the transform from the node to its environment via their closest common ancestor. This approach is compatible with CCRenderTexture so long as that common ancestor is a descendant of the render texture (either explicitly via the node graph or implicitly via the rendering of a node subtree via manual begin / visit / end). If instead the render texture is a descendant of the common ancestor then there is no way to always find a valid transform from the node to its environment.
1 parent d399c22 commit ec06df3

9 files changed

+154
-75
lines changed

cocos2d/CCEffectGlass.m

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -230,30 +230,53 @@ -(void)buildRenderPasses
230230
pass.shaderUniforms[weakSelf.uniformTranslationTable[@"u_refractEnvMap"]] = weakSelf.refractionEnvironment.texture ?: [CCTexture none];
231231
pass.shaderUniforms[weakSelf.uniformTranslationTable[@"u_reflectEnvMap"]] = weakSelf.reflectionEnvironment.texture ?: [CCTexture none];
232232

233-
// Setup the NDC space to refract environment space matrix.
234-
CGAffineTransform worldToRefractEnvTexture = CCEffectUtilsWorldToEnvironmentTransform(weakSelf.refractionEnvironment);
235-
GLKMatrix4 ndcToRefractEnvTextureMat = GLKMatrix4Multiply(CCEffectUtilsMat4FromAffineTransform(worldToRefractEnvTexture), pass.ndcToWorld);
236-
pass.shaderUniforms[weakSelf.uniformTranslationTable[@"u_ndcToRefractEnv"]] = [NSValue valueWithGLKMatrix4:ndcToRefractEnvTextureMat];
237233

238-
// Setup the NDC space to reflect environment space matrix.
239-
CGAffineTransform worldToReflectEnvTexture = CCEffectUtilsWorldToEnvironmentTransform(weakSelf.reflectionEnvironment);
240-
GLKMatrix4 ndcToReflectEnvTextureMat = GLKMatrix4Multiply(CCEffectUtilsMat4FromAffineTransform(worldToReflectEnvTexture), pass.ndcToWorld);
241-
pass.shaderUniforms[weakSelf.uniformTranslationTable[@"u_ndcToReflectEnv"]] = [NSValue valueWithGLKMatrix4:ndcToReflectEnvTextureMat];
234+
235+
// Get the transform from the affected node's local coordinates to the environment node.
236+
GLKMatrix4 effectNodeToRefractEnvNode = CCEffectUtilsTransformFromNodeToNode(pass.node, weakSelf.refractionEnvironment, nil);
237+
238+
// Concatenate the node to environment transform with the environment node to environment texture transform.
239+
// The result takes us from the affected node's coordinates to the environment's texture coordinates. We need
240+
// this when computing the tangent and normal vectors below.
241+
GLKMatrix4 effectNodeToRefractEnvTexture = GLKMatrix4Multiply(CCEffectUtilsMat4FromAffineTransform(weakSelf.refractionEnvironment.nodeToTextureTransform), effectNodeToRefractEnvNode);
242+
243+
// Concatenate the node to environment texture transform together with the transform from NDC to local node
244+
// coordinates. (NDC == normalized device coordinates == render target coordinates that are normalized to the
245+
// range 0..1). The shader uses this to map from NDC directly to environment texture coordinates.
246+
GLKMatrix4 ndcToRefractEnvTexture = GLKMatrix4Multiply(effectNodeToRefractEnvTexture, pass.ndcToNodeLocal);
247+
pass.shaderUniforms[weakSelf.uniformTranslationTable[@"u_ndcToRefractEnv"]] = [NSValue valueWithGLKMatrix4:ndcToRefractEnvTexture];
242248

243249
// Setup the tangent and binormal vectors for the refraction environment
244-
GLKVector4 refractTangent = CCEffectUtilsTangentInEnvironmentSpace(pass.transform, CCEffectUtilsMat4FromAffineTransform(worldToRefractEnvTexture));
250+
GLKVector4 refractTangent = GLKVector4Normalize(GLKMatrix4MultiplyVector4(effectNodeToRefractEnvTexture, GLKVector4Make(1.0f, 0.0f, 0.0f, 0.0f)));
245251
GLKVector4 refractNormal = GLKVector4Make(0.0f, 0.0f, 1.0f, 1.0f);
246252
GLKVector4 refractBinormal = GLKVector4CrossProduct(refractNormal, refractTangent);
247253
pass.shaderUniforms[weakSelf.uniformTranslationTable[@"u_refractTangent"]] = [NSValue valueWithGLKVector2:GLKVector2Make(refractTangent.x, refractTangent.y)];
248254
pass.shaderUniforms[weakSelf.uniformTranslationTable[@"u_refractBinormal"]] = [NSValue valueWithGLKVector2:GLKVector2Make(refractBinormal.x, refractBinormal.y)];
249255

250-
// Setup the tangent and binormal vectors for the reflection environment.
251-
GLKVector4 reflectTangent = CCEffectUtilsTangentInEnvironmentSpace(pass.transform, CCEffectUtilsMat4FromAffineTransform(worldToReflectEnvTexture));
256+
257+
258+
// Get the transform from the affected node's local coordinates to the environment node.
259+
GLKMatrix4 effectNodeToReflectEnvNode = CCEffectUtilsTransformFromNodeToNode(pass.node, weakSelf.reflectionEnvironment, nil);
260+
261+
// Concatenate the node to environment transform with the environment node to environment texture transform.
262+
// The result takes us from the affected node's coordinates to the environment's texture coordinates. We need
263+
// this when computing the tangent and normal vectors below.
264+
GLKMatrix4 effectNodeToReflectEnvTexture = GLKMatrix4Multiply(CCEffectUtilsMat4FromAffineTransform(weakSelf.reflectionEnvironment.nodeToTextureTransform), effectNodeToReflectEnvNode);
265+
266+
// Concatenate the node to environment texture transform together with the transform from NDC to local node
267+
// coordinates. (NDC == normalized device coordinates == render target coordinates that are normalized to the
268+
// range 0..1). The shader uses this to map from NDC directly to environment texture coordinates.
269+
GLKMatrix4 ndcToReflectEnvTexture = GLKMatrix4Multiply(effectNodeToReflectEnvTexture, pass.ndcToNodeLocal);
270+
pass.shaderUniforms[weakSelf.uniformTranslationTable[@"u_ndcToReflectEnv"]] = [NSValue valueWithGLKMatrix4:ndcToReflectEnvTexture];
271+
272+
// Setup the tangent and binormal vectors for the reflection environment
273+
GLKVector4 reflectTangent = GLKVector4Normalize(GLKMatrix4MultiplyVector4(effectNodeToReflectEnvTexture, GLKVector4Make(1.0f, 0.0f, 0.0f, 0.0f)));
252274
GLKVector4 reflectNormal = GLKVector4Make(0.0f, 0.0f, 1.0f, 1.0f);
253275
GLKVector4 reflectBinormal = GLKVector4CrossProduct(reflectNormal, reflectTangent);
254276
pass.shaderUniforms[weakSelf.uniformTranslationTable[@"u_reflectTangent"]] = [NSValue valueWithGLKVector2:GLKVector2Make(reflectTangent.x, reflectTangent.y)];
255277
pass.shaderUniforms[weakSelf.uniformTranslationTable[@"u_reflectBinormal"]] = [NSValue valueWithGLKVector2:GLKVector2Make(reflectBinormal.x, reflectBinormal.y)];
256-
278+
279+
257280
} copy]];
258281

259282
self.renderPasses = @[pass0];

cocos2d/CCEffectReflection.m

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,8 @@ -(void)buildFragmentFunctions
139139

140140
float fresnel = max(u_fresnelBias + (1.0 - u_fresnelBias) * pow((1.0 - nDotV), u_fresnelPower), 0.0);
141141

142-
// If the refracted texture coordinates are within the bounds of the environment map
143-
// blend the primary color with the refracted environment. Multiplying by the normal
142+
// If the reflected texture coordinates are within the bounds of the environment map
143+
// blend the primary color with the reflected environment. Multiplying by the normal
144144
// map alpha also allows the effect to be disabled for specific pixels.
145145
primaryColor += normalMap.a * fresnel * u_shininess * texture2D(u_envMap, reflectTexCoords);
146146
return primaryColor;
@@ -198,26 +198,28 @@ -(void)buildRenderPasses
198198

199199
pass.shaderUniforms[weakSelf.uniformTranslationTable[@"u_envMap"]] = weakSelf.environment.texture ?: [CCTexture none];
200200

201-
// Get the transform from world space to environment texture space. This is
202-
// concatenated with the current transform to move from local node space to
203-
// environment texture space.
204-
CGAffineTransform worldToReflectEnvTexture = CCEffectUtilsWorldToEnvironmentTransform(weakSelf.environment);
205201

206-
// Setup the tangent and binormal vectors for the refraction environment
207-
GLKVector4 reflectTangent = CCEffectUtilsTangentInEnvironmentSpace(pass.transform, CCEffectUtilsMat4FromAffineTransform(worldToReflectEnvTexture));
202+
// Get the transform from the affected node's local coordinates to the environment node.
203+
GLKMatrix4 effectNodeToReflectEnvNode = CCEffectUtilsTransformFromNodeToNode(pass.node, weakSelf.environment, nil);
204+
205+
// Concatenate the node to environment transform with the environment node to environment texture transform.
206+
// The result takes us from the affected node's coordinates to the environment's texture coordinates. We need
207+
// this when computing the tangent and normal vectors below.
208+
GLKMatrix4 effectNodeToReflectEnvTexture = GLKMatrix4Multiply(CCEffectUtilsMat4FromAffineTransform(weakSelf.environment.nodeToTextureTransform), effectNodeToReflectEnvNode);
209+
210+
// Concatenate the node to environment texture transform together with the transform from NDC to local node
211+
// coordinates. (NDC == normalized device coordinates == render target coordinates that are normalized to the
212+
// range 0..1). The shader uses this to map from NDC directly to environment texture coordinates.
213+
GLKMatrix4 ndcToReflectEnvTexture = GLKMatrix4Multiply(effectNodeToReflectEnvTexture, pass.ndcToNodeLocal);
214+
pass.shaderUniforms[weakSelf.uniformTranslationTable[@"u_ndcToEnv"]] = [NSValue valueWithGLKMatrix4:ndcToReflectEnvTexture];
215+
216+
// Setup the tangent and binormal vectors for the reflection environment
217+
GLKVector4 reflectTangent = GLKVector4Normalize(GLKMatrix4MultiplyVector4(effectNodeToReflectEnvTexture, GLKVector4Make(1.0f, 0.0f, 0.0f, 0.0f)));
208218
GLKVector4 reflectNormal = GLKVector4Make(0.0f, 0.0f, 1.0f, 1.0f);
209219
GLKVector4 reflectBinormal = GLKVector4CrossProduct(reflectNormal, reflectTangent);
210220
pass.shaderUniforms[weakSelf.uniformTranslationTable[@"u_tangent"]] = [NSValue valueWithGLKVector2:GLKVector2Make(reflectTangent.x, reflectTangent.y)];
211221
pass.shaderUniforms[weakSelf.uniformTranslationTable[@"u_binormal"]] = [NSValue valueWithGLKVector2:GLKVector2Make(reflectBinormal.x, reflectBinormal.y)];
212222

213-
// Setup the transform from normalized device coordinates (NDC, which is the space our vertex
214-
// positions are in) to environment texture space. We use this to compute environment texture
215-
// coordinates in the vertex shader.s
216-
GLKMatrix4 worldToReflectEnvTextureMat = CCEffectUtilsMat4FromAffineTransform(worldToReflectEnvTexture);
217-
GLKMatrix4 ndcToReflectEnvTextureMat = GLKMatrix4Multiply(worldToReflectEnvTextureMat, pass.ndcToWorld);
218-
219-
pass.shaderUniforms[weakSelf.uniformTranslationTable[@"u_ndcToEnv"]] = [NSValue valueWithGLKMatrix4:ndcToReflectEnvTextureMat];
220-
221223
} copy]];
222224

223225
self.renderPasses = @[pass0];

cocos2d/CCEffectRefraction.m

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -161,26 +161,27 @@ -(void)buildRenderPasses
161161
pass.shaderUniforms[weakSelf.uniformTranslationTable[@"u_refraction"]] = [NSNumber numberWithFloat:weakSelf.conditionedRefraction];
162162
pass.shaderUniforms[weakSelf.uniformTranslationTable[@"u_envMap"]] = weakSelf.environment.texture ?: [CCTexture none];
163163

164-
// Get the transform from world space to environment texture space. This is
165-
// concatenated with the current transform to move from local node space to
166-
// environment texture space.
167-
CGAffineTransform worldToRefractEnvTexture = CCEffectUtilsWorldToEnvironmentTransform(weakSelf.environment);
164+
// Get the transform from the affected node's local coordinates to the environment node.
165+
GLKMatrix4 effectNodeToRefractEnvNode = CCEffectUtilsTransformFromNodeToNode(pass.node, weakSelf.environment, nil);
166+
167+
// Concatenate the node to environment transform with the environment node to environment texture transform.
168+
// The result takes us from the affected node's coordinates to the environment's texture coordinates. We need
169+
// this when computing the tangent and normal vectors below.
170+
GLKMatrix4 effectNodeToRefractEnvTexture = GLKMatrix4Multiply(CCEffectUtilsMat4FromAffineTransform(weakSelf.environment.nodeToTextureTransform), effectNodeToRefractEnvNode);
171+
172+
// Concatenate the node to environment texture transform together with the transform from NDC to local node
173+
// coordinates. (NDC == normalized device coordinates == render target coordinates that are normalized to the
174+
// range 0..1). The shader uses this to map from NDC directly to environment texture coordinates.
175+
GLKMatrix4 ndcToRefractEnvTexture = GLKMatrix4Multiply(effectNodeToRefractEnvTexture, pass.ndcToNodeLocal);
176+
pass.shaderUniforms[weakSelf.uniformTranslationTable[@"u_ndcToEnv"]] = [NSValue valueWithGLKMatrix4:ndcToRefractEnvTexture];
168177

169178
// Setup the tangent and binormal vectors for the refraction environment
170-
GLKVector4 refractTangent = CCEffectUtilsTangentInEnvironmentSpace(pass.transform, CCEffectUtilsMat4FromAffineTransform(worldToRefractEnvTexture));
179+
GLKVector4 refractTangent = GLKVector4Normalize(GLKMatrix4MultiplyVector4(effectNodeToRefractEnvTexture, GLKVector4Make(1.0f, 0.0f, 0.0f, 0.0f)));
171180
GLKVector4 refractNormal = GLKVector4Make(0.0f, 0.0f, 1.0f, 1.0f);
172181
GLKVector4 refractBinormal = GLKVector4CrossProduct(refractNormal, refractTangent);
173182
pass.shaderUniforms[weakSelf.uniformTranslationTable[@"u_tangent"]] = [NSValue valueWithGLKVector2:GLKVector2Make(refractTangent.x, refractTangent.y)];
174183
pass.shaderUniforms[weakSelf.uniformTranslationTable[@"u_binormal"]] = [NSValue valueWithGLKVector2:GLKVector2Make(refractBinormal.x, refractBinormal.y)];
175184

176-
// Setup the transform from normalized device coordinates (NDC, which is the space our vertex
177-
// positions are in) to environment texture space. We use this to compute environment texture
178-
// coordinates in the vertex shader.s
179-
GLKMatrix4 worldToRefractEnvTextureMat = CCEffectUtilsMat4FromAffineTransform(worldToRefractEnvTexture);
180-
GLKMatrix4 ndcToRefractEnvTextureMat = GLKMatrix4Multiply(worldToRefractEnvTextureMat, pass.ndcToWorld);
181-
182-
pass.shaderUniforms[weakSelf.uniformTranslationTable[@"u_ndcToEnv"]] = [NSValue valueWithGLKMatrix4:ndcToRefractEnvTextureMat];
183-
184185
} copy]];
185186

186187
self.renderPasses = @[pass0];

cocos2d/CCEffectRenderer.m

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
#import "CCDirector.h"
1212
#import "CCEffect.h"
1313
#import "CCEffectStack.h"
14+
#import "CCEffectUtils.h"
1415
#import "CCTexture.h"
15-
#import "ccUtils.h"
1616

1717
#import "CCEffect_Private.h"
1818
#import "CCRenderer_Private.h"
@@ -249,6 +249,7 @@ -(void)drawSprite:(CCSprite *)sprite withEffect:(CCEffect *)effect uniforms:(NSM
249249
}
250250
renderPass.renderer = renderer;
251251
renderPass.renderPassId = i;
252+
renderPass.node = sprite;
252253

253254
if (fromIntermediate && (renderPass.indexInEffect == 0))
254255
{
@@ -288,10 +289,7 @@ -(void)drawSprite:(CCSprite *)sprite withEffect:(CCEffect *)effect uniforms:(NSM
288289
if (toFramebuffer)
289290
{
290291
renderPass.transform = *transform;
291-
292-
GLKMatrix4 ndcToWorldMat;
293-
[renderer.globalShaderUniforms[CCShaderUniformProjectionInv] getValue:&ndcToWorldMat];
294-
renderPass.ndcToWorld = ndcToWorldMat;
292+
renderPass.ndcToNodeLocal = GLKMatrix4Invert(*transform, nil);
295293

296294
[renderPass begin:previousPassTexture];
297295
[renderPass update];
@@ -305,15 +303,8 @@ -(void)drawSprite:(CCSprite *)sprite withEffect:(CCEffect *)effect uniforms:(NSM
305303
GLKMatrix4 invRenderTargetProjection = GLKMatrix4Invert(renderTargetProjection, &inverted);
306304
NSAssert(inverted, @"Unable to invert matrix.");
307305

308-
GLKMatrix4 invGlobalProjection;
309-
[renderer.globalShaderUniforms[CCShaderUniformProjectionInv] getValue:&invGlobalProjection];
310-
311-
GLKMatrix4 ndcToNodeMat = invRenderTargetProjection;
312-
GLKMatrix4 nodeToWorldMat = GLKMatrix4Multiply(invGlobalProjection, *transform);
313-
GLKMatrix4 ndcToWorldMat = GLKMatrix4Multiply(nodeToWorldMat, ndcToNodeMat);
314-
315306
renderPass.transform = renderTargetProjection;
316-
renderPass.ndcToWorld = ndcToWorldMat;
307+
renderPass.ndcToNodeLocal = invRenderTargetProjection;
317308

318309
CGSize rtSize = CGSizeMake((_contentSize.width + 2 * effect.padding.width) * _contentScale, (_contentSize.height + 2 * effect.padding.height) * _contentScale);
319310
rtSize.width = (rtSize.width <= 1.0f) ? 1.0f : rtSize.width;

cocos2d/CCEffectUtils.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,8 @@
88

99
#import "CCSprite.h"
1010

11+
GLKMatrix4 CCEffectUtilsTransformFromNodeToNode(CCNode *first, CCNode *second, BOOL *isPossible);
1112

12-
CGAffineTransform CCEffectUtilsWorldToEnvironmentTransform(CCSprite *environment);
13-
GLKVector4 CCEffectUtilsTangentInEnvironmentSpace(GLKMatrix4 effectToWorldMat, GLKMatrix4 worldToEnvMat);
1413
GLKMatrix4 CCEffectUtilsMat4FromAffineTransform(CGAffineTransform at);
1514
float CCEffectUtilsConditionRefraction(float refraction);
1615
float CCEffectUtilsConditionShininess(float shininess);

0 commit comments

Comments
 (0)