// // Copyright 2018 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // FogTest.cpp: Tests basic usage of glFog. #include "test_utils/ANGLETest.h" #include "test_utils/gl_raii.h" #include "util/random_utils.h" #include using namespace angle; class FogTest : public ANGLETest<> { protected: FogTest() { setWindowWidth(32); setWindowHeight(32); setConfigRedBits(8); setConfigGreenBits(8); setConfigBlueBits(8); setConfigAlphaBits(8); setConfigDepthBits(24); } }; // Initial state check. TEST_P(FogTest, InitialState) { EXPECT_GL_FALSE(glIsEnabled(GL_FOG)); EXPECT_GL_NO_ERROR(); GLint fogMode; glGetIntegerv(GL_FOG_MODE, &fogMode); EXPECT_GL_NO_ERROR(); EXPECT_GLENUM_EQ(GL_EXP, fogMode); GLfloat fogModeAsFloat; glGetFloatv(GL_FOG_MODE, &fogModeAsFloat); EXPECT_GL_NO_ERROR(); EXPECT_GLENUM_EQ(GL_EXP, fogModeAsFloat); GLfloat fogStart; GLfloat fogEnd; GLfloat fogDensity; glGetFloatv(GL_FOG_START, &fogStart); EXPECT_GL_NO_ERROR(); glGetFloatv(GL_FOG_END, &fogEnd); EXPECT_GL_NO_ERROR(); glGetFloatv(GL_FOG_DENSITY, &fogDensity); EXPECT_GL_NO_ERROR(); EXPECT_EQ(0.0f, fogStart); EXPECT_EQ(1.0f, fogEnd); EXPECT_EQ(1.0f, fogDensity); const GLColor32F kInitialFogColor(0.0f, 0.0f, 0.0f, 0.0f); GLColor32F initialFogColor; glGetFloatv(GL_FOG_COLOR, &initialFogColor.R); EXPECT_GL_NO_ERROR(); EXPECT_EQ(kInitialFogColor, initialFogColor); } // Negative test for parameter names. TEST_P(FogTest, NegativeParameter) { glFogfv(0, nullptr); EXPECT_GL_ERROR(GL_INVALID_ENUM); } // Negative test for parameter values. TEST_P(FogTest, NegativeValues) { glFogf(GL_FOG_MODE, 0.0f); EXPECT_GL_ERROR(GL_INVALID_VALUE); glFogf(GL_FOG_DENSITY, -1.0f); EXPECT_GL_ERROR(GL_INVALID_VALUE); } // Checks that fog state can be set. TEST_P(FogTest, Set) { GLfloat fogValue[4] = {}; glFogf(GL_FOG_MODE, GL_EXP2); EXPECT_GL_NO_ERROR(); glGetFloatv(GL_FOG_MODE, fogValue); EXPECT_GL_NO_ERROR(); EXPECT_GLENUM_EQ(GL_EXP2, fogValue[0]); glFogf(GL_FOG_DENSITY, 2.0f); EXPECT_GL_NO_ERROR(); glGetFloatv(GL_FOG_DENSITY, fogValue); EXPECT_GL_NO_ERROR(); EXPECT_GLENUM_EQ(2.0f, fogValue[0]); glFogf(GL_FOG_START, 2.0f); EXPECT_GL_NO_ERROR(); glGetFloatv(GL_FOG_START, fogValue); EXPECT_GL_NO_ERROR(); EXPECT_GLENUM_EQ(2.0f, fogValue[0]); glFogf(GL_FOG_END, 2.0f); EXPECT_GL_NO_ERROR(); glGetFloatv(GL_FOG_END, fogValue); EXPECT_GL_NO_ERROR(); EXPECT_GLENUM_EQ(2.0f, fogValue[0]); const GLColor32F testColor(0.1f, 0.2f, 0.3f, 0.4f); glFogfv(GL_FOG_COLOR, &testColor.R); EXPECT_GL_NO_ERROR(); glGetFloatv(GL_FOG_COLOR, fogValue); EXPECT_GL_NO_ERROR(); EXPECT_EQ(0.1f, fogValue[0]); EXPECT_EQ(0.2f, fogValue[1]); EXPECT_EQ(0.3f, fogValue[2]); EXPECT_EQ(0.4f, fogValue[3]); } class FogBlendTest : public ANGLETest<> { protected: FogBlendTest() { setWindowWidth(512); setWindowHeight(512); setConfigRedBits(8); setConfigGreenBits(8); setConfigBlueBits(8); setConfigAlphaBits(8); setConfigDepthBits(24); } }; // Draws a circle with a shadow effect using fog and blending. As seen in Street Fighter IV. TEST_P(FogBlendTest, ShadowEffect) { glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); int32_t w = getWindowWidth(); int32_t h = getWindowHeight(); int32_t radius = h / 4; std::vector colors; for (int32_t y = 0; y < h; y++) { for (int32_t x = 0; x < w; x++) { int32_t centerDistanceX = x - w / 2; int32_t centerDistanceY = y - h / 2; float centerDistance = sqrt(centerDistanceX * centerDistanceX + centerDistanceY * centerDistanceY); if (centerDistance > static_cast(radius)) { colors.push_back(GLColor::transparentBlack); } else { colors.push_back(GLColor::green); } } } GLTexture texture; glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, colors.data()); glClearColor(GLColor::red.R, GLColor::red.G, GLColor::red.B, GLColor::red.A); glClearDepthf(1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrthof(0, w, h, 0, 0, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glShadeModel(GL_FLAT); // TODO(http://anglebug.com/42266063): This currently renders incorrectly on ANGLE // Draw shadow effect glEnable(GL_FOG); const GLfloat fogColor[4] = {0.0f, 0.0f, 0.0f, 1.0f}; glFogfv(GL_FOG_COLOR, fogColor); glFogf(GL_FOG_MODE, GL_LINEAR); glFogf(GL_FOG_START, -1); glFogf(GL_FOG_END, -0.1); GLfloat shadowOffset = 32.0f; GLfloat wf = static_cast(w); GLfloat hf = static_cast(h); const GLfloat shadowPositions[8] = {shadowOffset, 0, shadowOffset, hf, wf + shadowOffset, 0, wf + shadowOffset, hf}; glVertexPointer(2, GL_FLOAT, 0, shadowPositions); const GLfloat texcoords[8] = {0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0}; glTexCoordPointer(2, GL_FLOAT, 0, texcoords); glColor4f(1, 1, 1, 0.6); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); // Draws color circle glDisable(GL_FOG); const GLfloat positions[8] = { 0, 0, 0, hf, wf, 0, wf, hf, }; glVertexPointer(2, GL_FLOAT, 0, positions); glTexCoordPointer(2, GL_FLOAT, 0, texcoords); glColor4f(1, 1, 1, 1); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); // The circle should be green EXPECT_PIXEL_COLOR_NEAR(w / 2, h / 2, GLColor::green, 0); // The background should be red EXPECT_PIXEL_COLOR_NEAR(w / 8, h / 8, GLColor::red, 0); EXPECT_PIXEL_COLOR_NEAR(w - w / 8, h / 8, GLColor::red, 0); EXPECT_PIXEL_COLOR_NEAR(w / 8, h - h / 8, GLColor::red, 0); EXPECT_PIXEL_COLOR_NEAR(w - w / 8, h - h / 8, GLColor::red, 0); // The shadow should be darkened red float shadowCenterX = w / 2 + radius + shadowOffset / 2.0f; EXPECT_PIXEL_COLOR_NEAR(shadowCenterX, h / 2, GLColor(102u, 0, 0, 194u), 0); } ANGLE_INSTANTIATE_TEST_ES1(FogTest); ANGLE_INSTANTIATE_TEST_ES1(FogBlendTest);