我有一个旧版 OpenGL 固定管道应用程序,已从 Windows(32 位)移植到 MacOS 64 位。
问题是,如果我有一个带有非位置光的场景,一切都会很好。如果我添加位置聚光灯,两个灯光会相互作用,并且会得到不正确的结果。
这是“正常”情况:聚光灯在右侧:
在这里,我将聚光灯向下移动(Y = 1)。注意立方体上的黑色区域。这是不正确。
现在,我通过注释掉聚光灯(灯 6)的“makeALight”调用来关闭聚光灯。现在,立方体被均匀照亮。
这是我用来生成灯光的测试代码。您需要使用brew 安装glfw 来构建它。
#define GL_SILENCE_DEPRECATION 1
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#include <GLFW/glfw3.h>
#include <iostream>
#define STB_IMAGE_IMPLEMENTATION
// Function to generate a checkerboard texture
GLuint generateCheckerboardTexture(int width, int height) {
int checkerSize = 8; // Size of each square in the checkerboard
GLubyte *data = new GLubyte[width * height * 3];
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
int checkerX = x / checkerSize;
int checkerY = y / checkerSize;
bool isWhite = (checkerX + checkerY) % 2 == 0;
GLubyte color = isWhite ? 255 : 0;
data[(y * width + x) * 3 + 0] = color; // Red
data[(y * width + x) * 3 + 1] = color; // Green
data[(y * width + x) * 3 + 2] = color; // Blue
}
}
GLuint textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);
// Set texture parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Upload the texture data
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
// Clean up
delete[] data;
return textureID;
}
// Function to create and configure a light source
void makeALight(int index, bool positional,
GLfloat posX, GLfloat posY, GLfloat posZ,
GLfloat dirX, GLfloat dirY, GLfloat dirZ,
GLfloat ambR, GLfloat ambG, GLfloat ambB, GLfloat ambA,
GLfloat diffR, GLfloat diffG, GLfloat diffB, GLfloat diffA,
GLfloat specR, GLfloat specG, GLfloat specB, GLfloat specA,
float beamWidth,
float constantA, float linearA, float quadA) {
GLenum glLightIndex = GL_LIGHT0 + index;
glEnable(glLightIndex);
GLfloat ambColor[] = {ambR, ambG, ambB, ambA};
GLfloat diffColor[] = {diffR, diffG, diffB, diffA};
GLfloat specColor[] = {specR, specG, specB, specA};
glLightfv(glLightIndex, GL_AMBIENT, ambColor);
glLightfv(glLightIndex, GL_DIFFUSE, diffColor);
glLightfv(glLightIndex, GL_SPECULAR, specColor);
glLightf(glLightIndex, GL_CONSTANT_ATTENUATION, constantA);
glLightf(glLightIndex, GL_LINEAR_ATTENUATION, linearA);
glLightf(glLightIndex, GL_QUADRATIC_ATTENUATION, quadA);
if (positional) {
GLfloat pos[] = {posX, posY, posZ, 1.0f};
glLightfv(glLightIndex, GL_POSITION, pos);
if (beamWidth != 180.0f) {
glLightf(glLightIndex, GL_SPOT_CUTOFF, beamWidth);
GLfloat direction[] = {dirX, dirY, dirZ};
glLightfv(glLightIndex, GL_SPOT_DIRECTION, direction);
} else {
glLightf(glLightIndex, GL_SPOT_CUTOFF, 180.0f);
}
} else {
GLfloat pos[] = {dirX, dirY, dirZ, 0.0f};
glLightfv(glLightIndex, GL_POSITION, pos);
}
}
// Function to render a textured cube
void renderTexturedCube(GLuint textureID) {
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, textureID);
glBegin(GL_QUADS);
// Front Face
glNormal3f(0.0f, 0.0f, 1.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
// Back Face
glNormal3f(0.0f, 0.0f, -1.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
// Top Face
glNormal3f(0.0f, 1.0f, 0.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);
// Bottom Face
glNormal3f(0.0f, -1.0f, 0.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);
// Right face
glNormal3f(1.0f, 0.0f, 0.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);
// Left Face
glNormal3f(-1.0f, 0.0f, 0.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);
glEnd();
glDisable(GL_TEXTURE_2D);
}
// Function to render a cube with explicit material properties
// Function to render a cube with explicit metallic material properties
void renderCube() {
// Define metallic material properties
GLfloat materialAmbient[] = {0.25f, 0.25f, 0.25f, 1.0f}; // Low ambient reflection
GLfloat materialDiffuse[] = {0.4f, 0.4f, 0.4f, 1.0f}; // Slightly higher diffuse reflection
GLfloat materialSpecular[] = {0.77f, 0.77f, 0.77f, 1.0f}; // High specular reflection
GLfloat materialShininess = 76.8f; // High shininess for a metallic look
// Apply the material properties to the cube
glMaterialfv(GL_FRONT, GL_AMBIENT, materialAmbient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, materialDiffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, materialSpecular);
glMaterialf(GL_FRONT, GL_SHININESS, materialShininess);
glBegin(GL_QUADS);
// Front Face
glNormal3f(0.0f, 0.0f, 1.0f);
glVertex3f(-1.0f, -1.0f, 1.0f);
glVertex3f(1.0f, -1.0f, 1.0f);
glVertex3f(1.0f, 1.0f, 1.0f);
glVertex3f(-1.0f, 1.0f, 1.0f);
// Back Face
glNormal3f(0.0f, 0.0f, -1.0f);
glVertex3f(-1.0f, -1.0f, -1.0f);
glVertex3f(-1.0f, 1.0f, -1.0f);
glVertex3f(1.0f, 1.0f, -1.0f);
glVertex3f(1.0f, -1.0f, -1.0f);
// Top Face
glNormal3f(0.0f, 1.0f, 0.0f);
glVertex3f(-1.0f, 1.0f, -1.0f);
glVertex3f(-1.0f, 1.0f, 1.0f);
glVertex3f(1.0f, 1.0f, 1.0f);
glVertex3f(1.0f, 1.0f, -1.0f);
// Bottom Face
glNormal3f(0.0f, -1.0f, 0.0f);
glVertex3f(-1.0f, -1.0f, -1.0f);
glVertex3f(1.0f, -1.0f, -1.0f);
glVertex3f(1.0f, -1.0f, 1.0f);
glVertex3f(-1.0f, -1.0f, 1.0f);
// Right Face
glNormal3f(1.0f, 0.0f, 0.0f);
glVertex3f(1.0f, -1.0f, -1.0f);
glVertex3f(1.0f, 1.0f, -1.0f);
glVertex3f(1.0f, 1.0f, 1.0f);
glVertex3f(1.0f, -1.0f, 1.0f);
// Left Face
glNormal3f(-1.0f, 0.0f, 0.0f);
glVertex3f(-1.0f, -1.0f, -1.0f);
glVertex3f(-1.0f, -1.0f, 1.0f);
glVertex3f(-1.0f, 1.0f, 1.0f);
glVertex3f(-1.0f, 1.0f, -1.0f);
glEnd();
}
int main() {
// Initialize GLFW
if (!glfwInit()) {
std::cerr << "Failed to initialize GLFW" << std::endl;
return -1;
}
// Create a windowed mode window and its OpenGL context
// Set the OpenGL version to 2.1
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_ANY_PROFILE);
GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL Lighting Test", nullptr, nullptr);
if (!window) {
std::cerr << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
// Make the window's context current
glfwMakeContextCurrent(window);
// Set viewport
glViewport(0, 0, 800, 600);
// Set up projection matrix
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f, 800.0f / 600.0f, 0.1f, 100.0f);
// Set up modelview matrix
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0.0f, 0.0f, 5.0f, // Eye position
0.0f, 0.0f, 0.0f, // Look-at point
0.0f, 1.0f, 0.0f); // Up vector
// Enable depth testing
glEnable(GL_DEPTH_TEST);
GLuint textureID = generateCheckerboardTexture(256, 256);
// Enable lighting
glEnable(GL_LIGHTING);
// Set shading model to smooth
glShadeModel(GL_SMOOTH);
//
float amb[] = { 0.70, 0.70, 0.70, 1 };
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, amb );
// Set up the lights using the provided makeALight calls
makeALight(6, true,
7.0f, 5.0f, 0.0f, // Position
0.0f, -1.0f, 0.0f, // Direction
1.0f, 0.00f, 0.00f, 1.0f, // Ambient
1.0f, 0.0f, 0.0f, 1.00f, // Diffuse
1.0f, 0.00f, 0.00f, 1.00f, // Specular
55.0f, // Angle
0.0f, 0.09f, 0.0f); // Constant, Linear, Quad
makeALight(0, false,
0.0f, 0.0f, 0.0f, // Position
0.00f, 0.00f, -1.00f, // Direction
0.0f, 1.0f, 0.0f, 1.00f, // Ambient
0.0f, 1.0f, 0.0f, 1.00f, // Diffuse
0.0f, 1.0f, 0.0f, 1.00f, // Specular
180.0f, // Angle
1.0f, 0.0f, 0.0f); // Constant, Linear, Quad
// Main loop
while (!glfwWindowShouldClose(window)) {
// Render here
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Rotate the cube over time
glPushMatrix();
glRotatef((float)glfwGetTime() * 50.0f, 1.0f, 1.0f, 0.0f);
// renderCube();
renderTexturedCube(textureID);
glPopMatrix();
// Swap front and back buffers
glfwSwapBuffers(window);
// Poll for and process events
glfwPollEvents();
}
// Clean up and exit
glDeleteTextures(1, &textureID);
glfwDestroyWindow(window);
glfwTerminate();
return 0;
}
所以我拿到了一台 Intel Mac(失败案例是在 Apple Silicon Mac 上),并且问题
没有发生。这些图像显示了不同的场景,但它们说明了问题。 X86 Mac 中的红色箭头显示位置灯处于打开状态,其照明锥体外部的纹理显示得很好,而在 Apple Silicon 外壳中,它们是深色/黑色的。 所以这意味着问题是:
我的 Rosetta 应用程序在苹果硅 Mac 上运行时出现数学问题。
英特尔 X86 机箱(有效!) ** 更新 2 **
我创建了我的测试程序(立方体)的纯 ARM64 版本并对其进行了测试。它
也失败了。 这消除了上面的选项 #1 - 问题可能
不是是由 Rosetta 将 X86-64 转换为 ARM64 引起的。 因此,这要么是我的代码触发的 Apple Silicon OpenGL 驱动程序行为,要么是片上 Apple Silicon 显卡的行为。
查看驱动程序,Intel Mac 使用 AppleIntelKBLGraphicsGLDriver,这是旧版 OGL 驱动程序。
但是,M1 Mac 使用 AppleMetalOpenGLRenderer,根据名称我认为它是 Metal API 的包装器。
由于这些是不同的驱动程序实现,我只能猜测您发现了一个错误。 如果您还没有向 Apple 报告,我建议您向 Apple 报告。
https://developer.apple.com/bug-reporting/ makeALight(6, true,
#if 0
7.0f, 5.0f, 0.0f, // Position (Normal light)
#else
7.0f, 1.0f, 0.0f, // Position (BUG: Incorrect shading with AppleMetalOpenGLRenderer)
#endif
0.0f, -1.0f, 0.0f, // Direction
1.0f, 0.00f, 0.00f, 1.0f, // Ambient
1.0f, 0.0f, 0.0f, 1.00f, // Diffuse
1.0f, 0.00f, 0.00f, 1.00f, // Specular
55.0f, // Angle
0.0f, 0.09f, 0.0f); // Constant, Linear, Quad