Skip to content

Commit eb86136

Browse files
committed
Merge pull request #1006 from thayerandrews/develop
CCEffects - Fix reflection map aliasing and add first pass at effect unit tests
2 parents d9b523f + a88244d commit eb86136

File tree

5 files changed

+323
-1
lines changed

5 files changed

+323
-1
lines changed

UnitTests/CCEffectTests.m

Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
//
2+
// CCEffectTests.m
3+
// cocos2d-tests-ios
4+
//
5+
// Created by Thayer J Andrews on 9/26/14.
6+
// Copyright (c) 2014 Cocos2d. All rights reserved.
7+
//
8+
9+
#import <XCTest/XCTest.h>
10+
#import "cocos2d.h"
11+
#import "CCEffectUtils.h"
12+
13+
@interface CCEffectTests : XCTestCase
14+
@end
15+
16+
@implementation CCEffectTests
17+
18+
-(void)testNodeAncestry
19+
{
20+
CCSprite *s1 = [CCSprite spriteWithImageNamed:@"f1.png"];
21+
CCSprite *s2 = [CCSprite spriteWithImageNamed:@"f1.png"];
22+
23+
BOOL commonAncestor = NO;
24+
25+
CCEffectUtilsTransformFromNodeToNode(s1, s2, &commonAncestor);
26+
XCTAssertFalse(commonAncestor, @"Common ancestor found where there is none.");
27+
28+
CCEffectUtilsTransformFromNodeToNode(s2, s1, &commonAncestor);
29+
XCTAssertFalse(commonAncestor, @"Common ancestor found where there is none.");
30+
}
31+
32+
-(void)testSameNode
33+
{
34+
CCSprite *s1 = [CCSprite spriteWithImageNamed:@"f1.png"];
35+
36+
BOOL commonAncestor = NO;
37+
GLKMatrix4 transform;
38+
39+
transform = CCEffectUtilsTransformFromNodeToNode(s1, s1, &commonAncestor);
40+
XCTAssertTrue(commonAncestor, @"No common ancestor found where there is one.");
41+
42+
XCTAssertEqual(transform.m00, 1.0f, @"Unexpected transform value.");
43+
XCTAssertEqual(transform.m01, 0.0f, @"Unexpected transform value.");
44+
XCTAssertEqual(transform.m02, 0.0f, @"Unexpected transform value.");
45+
XCTAssertEqual(transform.m03, 0.0f, @"Unexpected transform value.");
46+
47+
XCTAssertEqual(transform.m10, 0.0f, @"Unexpected transform value.");
48+
XCTAssertEqual(transform.m11, 1.0f, @"Unexpected transform value.");
49+
XCTAssertEqual(transform.m12, 0.0f, @"Unexpected transform value.");
50+
XCTAssertEqual(transform.m13, 0.0f, @"Unexpected transform value.");
51+
52+
XCTAssertEqual(transform.m20, 0.0f, @"Unexpected transform value.");
53+
XCTAssertEqual(transform.m21, 0.0f, @"Unexpected transform value.");
54+
XCTAssertEqual(transform.m22, 1.0f, @"Unexpected transform value.");
55+
XCTAssertEqual(transform.m23, 0.0f, @"Unexpected transform value.");
56+
57+
XCTAssertEqual(transform.m30, 0.0f, @"Unexpected transform value.");
58+
XCTAssertEqual(transform.m31, 0.0f, @"Unexpected transform value.");
59+
XCTAssertEqual(transform.m32, 0.0f, @"Unexpected transform value.");
60+
XCTAssertEqual(transform.m33, 1.0f, @"Unexpected transform value.");
61+
}
62+
63+
-(void)testSiblingTransforms
64+
{
65+
CCSprite *root = [CCSprite spriteWithImageNamed:@"f1.png"];
66+
root.name = @"root";
67+
root.positionType = CCPositionTypePoints;
68+
root.position = ccp(0.0f, 0.0f);
69+
root.anchorPoint = ccp(0.0f, 0.0f);
70+
71+
CCSprite *s1 = [CCSprite spriteWithImageNamed:@"f1.png"];
72+
s1.name = @"s1";
73+
s1.positionType = CCPositionTypePoints;
74+
s1.position = ccp(10.0f, 10.0f);
75+
s1.anchorPoint = ccp(0.0f, 0.0f);
76+
77+
CCSprite *s2 = [CCSprite spriteWithImageNamed:@"f1.png"];
78+
s2.name = @"s2";
79+
s2.positionType = CCPositionTypePoints;
80+
s2.position = ccp(100.0f, 100.0f);
81+
s2.anchorPoint = ccp(0.0f, 0.0f);
82+
83+
CCSprite *s3 = [CCSprite spriteWithImageNamed:@"f1.png"];
84+
s3.name = @"s3";
85+
s3.positionType = CCPositionTypePoints;
86+
s3.position = ccp(1000.0f, 1000.0f);
87+
s3.anchorPoint = ccp(0.0f, 0.0f);
88+
89+
BOOL commonAncestor = NO;
90+
GLKMatrix4 transform;
91+
92+
93+
94+
// Test this hierarchy:
95+
//
96+
// root
97+
// \
98+
// s1
99+
// / \
100+
// s2 s3
101+
//
102+
[root addChild:s1];
103+
[s1 addChild:s2];
104+
[s1 addChild:s3];
105+
transform = CCEffectUtilsTransformFromNodeToNode(s1, s2, &commonAncestor);
106+
XCTAssertTrue(commonAncestor, @"No common ancestor found where there is one.");
107+
XCTAssertEqual(transform.m30, -100.0f, @"");
108+
XCTAssertEqual(transform.m31, -100.0f, @"");
109+
110+
transform = CCEffectUtilsTransformFromNodeToNode(s2, s1, &commonAncestor);
111+
XCTAssertTrue(commonAncestor, @"No common ancestor found where there is one.");
112+
XCTAssertEqual(transform.m30, 100.0f, @"");
113+
XCTAssertEqual(transform.m31, 100.0f, @"");
114+
115+
transform = CCEffectUtilsTransformFromNodeToNode(s2, s3, &commonAncestor);
116+
XCTAssertTrue(commonAncestor, @"No common ancestor found where there is one.");
117+
XCTAssertEqual(transform.m30, -900.0f, @"");
118+
XCTAssertEqual(transform.m31, -900.0f, @"");
119+
120+
transform = CCEffectUtilsTransformFromNodeToNode(s3, s2, &commonAncestor);
121+
XCTAssertTrue(commonAncestor, @"No common ancestor found where there is one.");
122+
XCTAssertEqual(transform.m30, 900.0f, @"");
123+
XCTAssertEqual(transform.m31, 900.0f, @"");
124+
125+
transform = CCEffectUtilsTransformFromNodeToNode(s1, s3, &commonAncestor);
126+
XCTAssertTrue(commonAncestor, @"No common ancestor found where there is one.");
127+
XCTAssertEqual(transform.m30, -1000.0f, @"");
128+
XCTAssertEqual(transform.m31, -1000.0f, @"");
129+
130+
transform = CCEffectUtilsTransformFromNodeToNode(s3, s1, &commonAncestor);
131+
XCTAssertTrue(commonAncestor, @"No common ancestor found where there is one.");
132+
XCTAssertEqual(transform.m30, 1000.0f, @"");
133+
XCTAssertEqual(transform.m31, 1000.0f, @"");
134+
135+
136+
// Test this hierarchy:
137+
//
138+
// s1
139+
// / \
140+
// s2 s3
141+
//
142+
[root removeChild:s1];
143+
transform = CCEffectUtilsTransformFromNodeToNode(s1, s2, &commonAncestor);
144+
XCTAssertTrue(commonAncestor, @"No common ancestor found where there is one.");
145+
XCTAssertEqual(transform.m30, -100.0f, @"");
146+
XCTAssertEqual(transform.m31, -100.0f, @"");
147+
148+
transform = CCEffectUtilsTransformFromNodeToNode(s2, s1, &commonAncestor);
149+
XCTAssertTrue(commonAncestor, @"No common ancestor found where there is one.");
150+
XCTAssertEqual(transform.m30, 100.0f, @"");
151+
XCTAssertEqual(transform.m31, 100.0f, @"");
152+
153+
transform = CCEffectUtilsTransformFromNodeToNode(s2, s3, &commonAncestor);
154+
XCTAssertTrue(commonAncestor, @"No common ancestor found where there is one.");
155+
XCTAssertEqual(transform.m30, -900.0f, @"");
156+
XCTAssertEqual(transform.m31, -900.0f, @"");
157+
158+
transform = CCEffectUtilsTransformFromNodeToNode(s3, s2, &commonAncestor);
159+
XCTAssertTrue(commonAncestor, @"No common ancestor found where there is one.");
160+
XCTAssertEqual(transform.m30, 900.0f, @"");
161+
XCTAssertEqual(transform.m31, 900.0f, @"");
162+
163+
transform = CCEffectUtilsTransformFromNodeToNode(s1, s3, &commonAncestor);
164+
XCTAssertTrue(commonAncestor, @"No common ancestor found where there is one.");
165+
XCTAssertEqual(transform.m30, -1000.0f, @"");
166+
XCTAssertEqual(transform.m31, -1000.0f, @"");
167+
168+
transform = CCEffectUtilsTransformFromNodeToNode(s3, s1, &commonAncestor);
169+
XCTAssertTrue(commonAncestor, @"No common ancestor found where there is one.");
170+
XCTAssertEqual(transform.m30, 1000.0f, @"");
171+
XCTAssertEqual(transform.m31, 1000.0f, @"");
172+
173+
[s1 removeChild:s2];
174+
[s1 removeChild:s3];
175+
}
176+
177+
- (void)testAncestorTransforms
178+
{
179+
CCSprite *root = [CCSprite spriteWithImageNamed:@"f1.png"];
180+
root.name = @"root";
181+
root.positionType = CCPositionTypePoints;
182+
root.position = ccp(0.0f, 0.0f);
183+
root.anchorPoint = ccp(0.0f, 0.0f);
184+
185+
CCSprite *s1 = [CCSprite spriteWithImageNamed:@"f1.png"];
186+
s1.name = @"s1";
187+
s1.positionType = CCPositionTypePoints;
188+
s1.position = ccp(10.0f, 10.0f);
189+
s1.anchorPoint = ccp(0.0f, 0.0f);
190+
191+
CCSprite *s2 = [CCSprite spriteWithImageNamed:@"f1.png"];
192+
s2.name = @"s2";
193+
s2.positionType = CCPositionTypePoints;
194+
s2.position = ccp(100.0f, 100.0f);
195+
s2.anchorPoint = ccp(0.0f, 0.0f);
196+
197+
CCSprite *s3 = [CCSprite spriteWithImageNamed:@"f1.png"];
198+
s3.name = @"s3";
199+
s3.positionType = CCPositionTypePoints;
200+
s3.position = ccp(1000.0f, 1000.0f);
201+
s3.anchorPoint = ccp(0.0f, 0.0f);
202+
203+
BOOL commonAncestor = NO;
204+
GLKMatrix4 transform;
205+
206+
207+
// Test this hierarchy:
208+
//
209+
// root
210+
// \
211+
// s1
212+
// \
213+
// s2
214+
// \
215+
// s3
216+
//
217+
[root addChild:s1];
218+
[s1 addChild:s2];
219+
[s2 addChild:s3];
220+
transform = CCEffectUtilsTransformFromNodeToNode(s1, s2, &commonAncestor);
221+
XCTAssertTrue(commonAncestor, @"No common ancestor found where there is one.");
222+
XCTAssertEqual(transform.m30, -100.0f, @"");
223+
XCTAssertEqual(transform.m31, -100.0f, @"");
224+
225+
transform = CCEffectUtilsTransformFromNodeToNode(s2, s1, &commonAncestor);
226+
XCTAssertTrue(commonAncestor, @"No common ancestor found where there is one.");
227+
XCTAssertEqual(transform.m30, 100.0f, @"");
228+
XCTAssertEqual(transform.m31, 100.0f, @"");
229+
230+
transform = CCEffectUtilsTransformFromNodeToNode(s2, s3, &commonAncestor);
231+
XCTAssertTrue(commonAncestor, @"No common ancestor found where there is one.");
232+
XCTAssertEqual(transform.m30, -1000.0f, @"");
233+
XCTAssertEqual(transform.m31, -1000.0f, @"");
234+
235+
transform = CCEffectUtilsTransformFromNodeToNode(s3, s2, &commonAncestor);
236+
XCTAssertTrue(commonAncestor, @"No common ancestor found where there is one.");
237+
XCTAssertEqual(transform.m30, 1000.0f, @"");
238+
XCTAssertEqual(transform.m31, 1000.0f, @"");
239+
240+
transform = CCEffectUtilsTransformFromNodeToNode(s1, s3, &commonAncestor);
241+
XCTAssertTrue(commonAncestor, @"No common ancestor found where there is one.");
242+
XCTAssertEqual(transform.m30, -1100.0f, @"");
243+
XCTAssertEqual(transform.m31, -1100.0f, @"");
244+
245+
transform = CCEffectUtilsTransformFromNodeToNode(s3, s1, &commonAncestor);
246+
XCTAssertTrue(commonAncestor, @"No common ancestor found where there is one.");
247+
XCTAssertEqual(transform.m30, 1100.0f, @"");
248+
XCTAssertEqual(transform.m31, 1100.0f, @"");
249+
250+
251+
// Test this hierarchy:
252+
//
253+
// s1
254+
// \
255+
// s2
256+
// \
257+
// s3
258+
//
259+
[root removeChild:s1];
260+
transform = CCEffectUtilsTransformFromNodeToNode(s1, s2, &commonAncestor);
261+
XCTAssertTrue(commonAncestor, @"No common ancestor found where there is one.");
262+
XCTAssertEqual(transform.m30, -100.0f, @"");
263+
XCTAssertEqual(transform.m31, -100.0f, @"");
264+
265+
transform = CCEffectUtilsTransformFromNodeToNode(s2, s1, &commonAncestor);
266+
XCTAssertTrue(commonAncestor, @"No common ancestor found where there is one.");
267+
XCTAssertEqual(transform.m30, 100.0f, @"");
268+
XCTAssertEqual(transform.m31, 100.0f, @"");
269+
270+
transform = CCEffectUtilsTransformFromNodeToNode(s2, s3, &commonAncestor);
271+
XCTAssertTrue(commonAncestor, @"No common ancestor found where there is one.");
272+
XCTAssertEqual(transform.m30, -1000.0f, @"");
273+
XCTAssertEqual(transform.m31, -1000.0f, @"");
274+
275+
transform = CCEffectUtilsTransformFromNodeToNode(s3, s2, &commonAncestor);
276+
XCTAssertTrue(commonAncestor, @"No common ancestor found where there is one.");
277+
XCTAssertEqual(transform.m30, 1000.0f, @"");
278+
XCTAssertEqual(transform.m31, 1000.0f, @"");
279+
280+
transform = CCEffectUtilsTransformFromNodeToNode(s1, s3, &commonAncestor);
281+
XCTAssertTrue(commonAncestor, @"No common ancestor found where there is one.");
282+
XCTAssertEqual(transform.m30, -1100.0f, @"");
283+
XCTAssertEqual(transform.m31, -1100.0f, @"");
284+
285+
transform = CCEffectUtilsTransformFromNodeToNode(s3, s1, &commonAncestor);
286+
XCTAssertTrue(commonAncestor, @"No common ancestor found where there is one.");
287+
XCTAssertEqual(transform.m30, 1100.0f, @"");
288+
XCTAssertEqual(transform.m31, 1100.0f, @"");
289+
[s1 removeChild:s2];
290+
[s2 removeChild:s3];
291+
}
292+
293+
@end

cocos2d-tests-ios.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
92324E2A18EB635500D78D3F /* CCReaderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 92324E2918EB635500D78D3F /* CCReaderTest.m */; };
3030
9269312A1923D8A700CE6285 /* Resources-shared in Resources */ = {isa = PBXBuildFile; fileRef = B7C6237517EA695100928F91 /* Resources-shared */; };
3131
92FE241118F5F06F00647961 /* CCAnimationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 92FE241018F5F06F00647961 /* CCAnimationTest.m */; };
32+
9D96557319D6113500428E79 /* CCEffectTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D96557219D6113500428E79 /* CCEffectTests.m */; };
3233
A6167B93189A7D4D0044D391 /* VertexZTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A6167B92189A7D4D0044D391 /* VertexZTest.m */; };
3334
A664A4EF18A3D9B8006184B8 /* PositioningTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A664A4EE18A3D9B8006184B8 /* PositioningTest.m */; };
3435
B70AFC30180F2D7400516435 /* CCTransitionTest.m in Sources */ = {isa = PBXBuildFile; fileRef = B70AFC2F180F2D7400516435 /* CCTransitionTest.m */; };
@@ -225,6 +226,7 @@
225226
75F76496185A831B00E2FAFE /* Sounds */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Sounds; path = Resources/Sounds; sourceTree = SOURCE_ROOT; };
226227
92324E2918EB635500D78D3F /* CCReaderTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCReaderTest.m; sourceTree = "<group>"; };
227228
92FE241018F5F06F00647961 /* CCAnimationTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCAnimationTest.m; sourceTree = "<group>"; };
229+
9D96557219D6113500428E79 /* CCEffectTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCEffectTests.m; sourceTree = "<group>"; };
228230
A6167B92189A7D4D0044D391 /* VertexZTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = VertexZTest.m; path = "cocos2d-ui-tests/tests/VertexZTest.m"; sourceTree = SOURCE_ROOT; };
229231
A664A4EE18A3D9B8006184B8 /* PositioningTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PositioningTest.m; path = "cocos2d-ui-tests/tests/PositioningTest.m"; sourceTree = SOURCE_ROOT; };
230232
B70AFC2F180F2D7400516435 /* CCTransitionTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CCTransitionTest.m; path = "cocos2d-ui-tests/tests/CCTransitionTest.m"; sourceTree = SOURCE_ROOT; };
@@ -398,6 +400,7 @@
398400
isa = PBXGroup;
399401
children = (
400402
92FE241018F5F06F00647961 /* CCAnimationTest.m */,
403+
9D96557219D6113500428E79 /* CCEffectTests.m */,
401404
755569E31856361100ED1B0F /* CCFileUtilTests.m */,
402405
755569E41856361100ED1B0F /* CCNodeTests.m */,
403406
755569E51856361100ED1B0F /* CCPhysicsTests.m */,
@@ -934,6 +937,7 @@
934937
files = (
935938
92324E2A18EB635500D78D3F /* CCReaderTest.m in Sources */,
936939
D32FDE8619B645CA0078CC16 /* CCTextureTests.m in Sources */,
940+
9D96557319D6113500428E79 /* CCEffectTests.m in Sources */,
937941
75556A161856370A00ED1B0F /* CCFileUtilTests.m in Sources */,
938942
92FE241118F5F06F00647961 /* CCAnimationTest.m in Sources */,
939943
75556A171856370A00ED1B0F /* CCNodeTests.m in Sources */,

cocos2d/CCEffectGlass.m

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,20 @@ -(void)buildFragmentFunctions
160160
// map alpha also allows the effect to be disabled for specific pixels.
161161
vec4 refraction = normalMap.a * inBounds * texture2D(u_refractEnvMap, refractTexCoords) * (1.0 - primaryColor.a);
162162

163+
// Compute Schlick's approximation (http://en.wikipedia.org/wiki/Schlick's_approximation) of the
164+
// fresnel reflectance.
165+
float fresnel = max(u_fresnelBias + (1.0 - u_fresnelBias) * pow((1.0 - nDotV), u_fresnelPower), 0.0);
166+
167+
// Apply a cutoff to nDotV to reduce the aliasing that occurs in the reflected
168+
// image. As the surface normal approaches a 90 degree angle relative to the viewing
169+
// direction, the sampling of the reflection map becomes more and more compressed
170+
// which can lead to undesirable aliasing artifacts. The cutoff threshold reduces
171+
// the contribution of these pixels to the final image and hides this aliasing.
172+
const float NDOTV_CUTOFF = 0.2;
173+
fresnel *= smoothstep(0.0, NDOTV_CUTOFF, nDotV);
174+
163175
// Add the reflected color modulated by the fresnel term. Multiplying by the normal
164176
// map alpha also allows the effect to be disabled for specific pixels.
165-
float fresnel = max(u_fresnelBias + (1.0 - u_fresnelBias) * pow((1.0 - nDotV), u_fresnelPower), 0.0);
166177
vec4 reflection = normalMap.a * fresnel * u_shininess * texture2D(u_reflectEnvMap, reflectTexCoords);
167178

168179
return primaryColor + refraction + reflection;

cocos2d/CCEffectReflection.m

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,18 @@ -(void)buildFragmentFunctions
137137
// Compute the combination of the sprite's color and texture.
138138
vec4 primaryColor = inputValue;
139139

140+
// Compute Schlick's approximation (http://en.wikipedia.org/wiki/Schlick's_approximation) of the
141+
// fresnel reflectance.
140142
float fresnel = max(u_fresnelBias + (1.0 - u_fresnelBias) * pow((1.0 - nDotV), u_fresnelPower), 0.0);
141143

144+
// Apply a cutoff to nDotV to reduce the aliasing that occurs in the reflected
145+
// image. As the surface normal approaches a 90 degree angle relative to the viewing
146+
// direction, the sampling of the reflection map becomes more and more compressed
147+
// which can lead to undesirable aliasing artifacts. The cutoff threshold reduces
148+
// the contribution of these pixels to the final image and hides this aliasing.
149+
const float NDOTV_CUTOFF = 0.2;
150+
fresnel *= smoothstep(0.0, NDOTV_CUTOFF, nDotV);
151+
142152
// If the reflected texture coordinates are within the bounds of the environment map
143153
// blend the primary color with the reflected environment. Multiplying by the normal
144154
// map alpha also allows the effect to be disabled for specific pixels.

cocos2d/CCEffectUtils.m

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ GLKMatrix4 CCEffectUtilsTransformFromNodeToNode(CCNode *first, CCNode *second, B
4848
{
4949
*isPossible = (commonAncestor != nil);
5050
}
51+
if (commonAncestor == nil)
52+
{
53+
return GLKMatrix4Identity;
54+
}
5155

5256
// Compute the transform from this node to the common ancestor
5357
CGAffineTransform t1 = [first nodeToParentTransform];

0 commit comments

Comments
 (0)