C++ 抓老鼠小姐
在这些课程中,我用鼠标拾取地形(但使用的是c++) 问题在于鼠标的位置与光线与岩层相交的位置不对应: 在垂直方向上有一个大错误,在水平方向上有一个小错误。 不要看阴影,这不是法线的校正贴图。 有什么不对劲?我的代码:C++ 抓老鼠小姐,c++,opengl,coordinate-transformation,C++,Opengl,Coordinate Transformation,在这些课程中,我用鼠标拾取地形(但使用的是c++) 问题在于鼠标的位置与光线与岩层相交的位置不对应: 在垂直方向上有一个大错误,在水平方向上有一个小错误。 不要看阴影,这不是法线的校正贴图。 有什么不对劲?我的代码: void MousePicker::update() { view = cam->getViewMatrix(); currentRay = calculateMouseRay(); if (intersectionInRange(0, RAY_RAN
void MousePicker::update() {
view = cam->getViewMatrix();
currentRay = calculateMouseRay();
if (intersectionInRange(0, RAY_RANGE, currentRay)) {
currentTerrainPoint = binarySearch(0, 0, RAY_RANGE, currentRay);
}
else {
currentTerrainPoint = vec3();
}
}
vec3 MousePicker::calculateMouseRay() {
glfwGetCursorPos(win, &mouseInfo.xPos, &mouseInfo.yPos);
vec2 normalizedCoords = getNormalizedCoords(mouseInfo.xPos, mouseInfo.yPos);
vec4 clipCoords = vec4(normalizedCoords.x, normalizedCoords.y, -1.0f, 1.0f);
vec4 eyeCoords = toEyeCoords(clipCoords);
vec3 worldRay = toWorldCoords(eyeCoords);
return worldRay;
}
vec2 MousePicker::getNormalizedCoords(double xPos, double yPos) {
GLint width, height;
glfwGetWindowSize(win, &width, &height);
//GLfloat x = (2.0 * xPos) / width - 1.0f;
GLfloat x = -((width - xPos) / width - 0.5f) * 2.0f;
//GLfloat y = 1.0f - (2.0f * yPos) / height;
GLfloat y = ((height - yPos) / height - 0.5f) * 2.0f;
//float z = 1.0f;
mouseInfo.normalizedCoords = vec2(x, y);
return vec2(x,y);
}
vec4 MousePicker::toEyeCoords(vec4 clipCoords) {
vec4 invertedProjection = inverse(projection) * clipCoords;
//vec4 eyeCoords = translate(invertedProjection, clipCoords);
mouseInfo.eyeCoords = vec4(invertedProjection.x, invertedProjection.y, -1.0f, 0.0f);
return vec4(invertedProjection.x, invertedProjection.y, -1.0f, 0.0f);
}
vec3 MousePicker::toWorldCoords(vec4 eyeCoords) {
vec3 rayWorld = vec3(inverse(view) * eyeCoords);
vec3 mouseRay = vec3(rayWorld.x, rayWorld.y, rayWorld.z);
rayWorld = normalize(rayWorld);
mouseInfo.worldRay = rayWorld;
return rayWorld;
}
//*********************************************************************************
vec3 MousePicker::getPointOnRay(vec3 ray, float distance) {
vec3 camPos = cam->getCameraPos();
vec3 start = vec3(camPos.x, camPos.y, camPos.z);
vec3 scaledRay = vec3(ray.x * distance, ray.y * distance, ray.z * distance);
return vec3(start + scaledRay);
}
vec3 MousePicker::binarySearch(int count, float start, float finish, vec3 ray) {
float half = start + ((finish - start) / 2.0f);
if (count >= RECURSION_COUNT) {
vec3 endPoint = getPointOnRay(ray, half);
//Terrain* ter = &getTerrain(endPoint.x, endPoint.z);
if (terrain != NULL) {
return endPoint;
}
else {
return vec3();
}
}
if (intersectionInRange(start, half, ray)) {
return binarySearch(count + 1, start, half, ray);
}
else {
return binarySearch(count + 1, half, finish, ray);
}
}
bool MousePicker::intersectionInRange(float start, float finish, vec3 ray) {
vec3 startPoint = getPointOnRay(ray, start);
vec3 endPoint = getPointOnRay(ray, finish);
if (!isUnderGround(startPoint) && isUnderGround(endPoint)) {
return true;
}
else {
return false;
}
}
bool MousePicker::isUnderGround(vec3 testPoint) {
//Terrain* ter = &getTerrain(testPoint.x, testPoint.z);
float height = 0;
if (terrain != NULL) {
height = terrain->getHeightPoint(testPoint.x, testPoint.z);
mouseInfo.height = height;
}
if (testPoint.y < height) {
return true;
}
else {
return false;
}
}
Terrain MousePicker::getTerrain(float worldX, float worldZ) {
return *terrain;
}
void MousePicker::update(){
view=cam->getViewMatrix();
currentRay=calculateMouseRay();
if(相交范围(0,光线范围,当前光线)){
currentTerrainPoint=binarySearch(0,0,光线范围,currentRay);
}
否则{
currentTerrainPoint=vec3();
}
}
vec3 MousePicker::calculateMouseRay(){
glfwGetCursorPos(win,&mouseInfo.xPos,&mouseInfo.yPos);
vec2 normalizedCoords=getNormalizedCoords(mouseInfo.xPos、mouseInfo.yPos);
vec4 clipCoords=vec4(normalizedCoords.x,normalizedCoords.y,-1.0f,1.0f);
vec4-eyecords=toeyecords(clipCoords);
vec3 worldRay=toWorldCoords(eyeCoords);
返回世界射线;
}
vec2 MousePicker::getNormalizedCoords(双XPO,双YPO){
闪烁宽度、高度;
GLFWGetWindowsSize(win、宽度和高度);
//GLfloat x=(2.0*xPos)/宽度-1.0f;
GLX=-((宽度-xPos)/宽度-0.5f)*2.0f;
//GLY=1.0f-(2.0f*yPos)/高度;
GLY=((高度-yPos)/高度-0.5f)*2.0f;
//浮动z=1.0f;
mouseInfo.normalizedCoords=vec2(x,y);
返回向量2(x,y);
}
vec4鼠标指针::toeyecords(vec4 clipCoords){
vec4逆变器投影=逆(投影)*clipCoords;
//vec4 eyeCoords=平移(反相器投影、clipCoords);
mouseInfo.eyeCoords=vec4(invertedProjection.x,invertedProjection.y,-1.0f,0.0f);
返回向量4(invertedProjection.x,invertedProjection.y,-1.0f,0.0f);
}
VEC3MousePicker::toWorldCoords(VEC4EyeCoords){
vec3光线世界=vec3(反向(视图)*眼坐标);
vec3 mouseRay=vec3(rayWorld.x,rayWorld.y,rayWorld.z);
光线世界=正常化(光线世界);
mouseInfo.worldRay=光线世界;
回归世界;
}
//*********************************************************************************
vec3鼠标Epicker::getPointOnRay(vec3光线,浮动距离){
vec3 camPos=cam->getCameraPos();
vec3 start=vec3(camPos.x、camPos.y、camPos.z);
vec3 scaledRay=vec3(光线x*距离,光线y*距离,光线z*距离);
返回vec3(开始+缩放播放);
}
vec3 MousePicker::binarySearch(整数计数、浮点开始、浮点结束、vec3射线){
浮动一半=开始+((完成-开始)/2.0f);
如果(计数>=递归计数){
vec3端点=getPointOnRay(光线,一半);
//地形*ter=&getTerrain(endPoint.x,endPoint.z);
如果(地形!=NULL){
返回端点;
}
否则{
返回vec3();
}
}
if(相交范围(起点、一半、射线)){
返回二进制搜索(计数+1,开始,一半,光线);
}
否则{
返回二进制搜索(计数+1,一半,完成,光线);
}
}
bool MousePicker::intersectionRange(浮动开始、浮动结束、vec3射线){
vec3 startPoint=getPointOnRay(光线,开始);
vec3端点=getPointOnRay(光线,完成);
如果(!isUnderGround(起点)和&isUnderGround(终点)){
返回true;
}
否则{
返回false;
}
}
bool MousePicker::isUnderGround(vec3测试点){
//地形*ter=&getTerrain(testPoint.x,testPoint.z);
浮动高度=0;
如果(地形!=NULL){
高度=地形->获取高度点(testPoint.x,testPoint.z);
mouseInfo.height=高度;
}
if(测试点y<高度){
返回true;
}
否则{
返回false;
}
}
地形鼠标器::getTerrain(float-worldX,float-worldZ){
返回*地形;
}
在透视投影中,从眼睛位置到屏幕上某个点的光线可以由2个点定义。第一个点是视图空间中(0,0,0)的眼睛(相机)位置。必须根据屏幕上的位置计算第二个点。屏幕位置必须转换为(-1,-1)到(1,1)范围内的标准化设备坐标 要计算光线上穿过相机位置和屏幕上点的点,必须知道透视投影的视野和纵横比:
fov_y = vertical field of view angle in radians
aspect = w / h
GLfloat tanFov = tan( fov_y * 0.5 );
glm::vec3 ray_P = vec3( ndc_x * aspect * tanFov, ndc_y * tanFov, -1.0 ) );
从相机位置到屏幕上某个点的光线可以通过世界空间中的以下位置(P0
)和标准化方向(dir
)来定义:
view = view matrix
glm::mat4 invView = glm::inverse( view );
glm::vec3 P0 = invView * glm::vec3(0.0f, 0.0f, 0.0f);
// = glm::vec3( view[3][0], view[3][1], view[3][2] );
glm::vec3 dir = glm::normalize( invView * ray_P - P0 );
vec3 MousePicker::calculateMouseRay( void ) {
glfwGetCursorPos(win, &mouseInfo.xPos, &mouseInfo.yPos);
vec2 normalizedCoords = getNormalizedCoords(mouseInfo.xPos, mouseInfo.yPos);
ray_Px = normalizedCoords.x / projection[0][0]; // projection[0][0] == 1.0 / (tanFov * aspect)
ray_Py = normalizedCoords.y / projection[1][1]; // projection[1][1] == 1.0 / tanFov
glm::vec3 ray_P = vec3( ray_Px, ray_Py, -1.0f ) );
vec3 camPos = cam->getCameraPos(); // == glm::vec3( view[3][0], view[3][1], view[3][2] );
glm::mat4 invView = glm::inverse( view );
glm::vec3 P0 = camPos;
glm::vec3 dir = glm::normalize( invView * ray_P - P0 );
return dir;
}
在这种情况下,以下问题的答案也会很有趣:
r = right, l = left, b = bottom, t = top, n = near, f = far
2*n/(r-l) 0 0 0
0 2*n/(t-b) 0 0
(r+l)/(r-l) (t+b)/(t-b) -(f+n)/(f-n) -1
0 0 -2*f*n/(f-n) 0
内容如下:
aspect = w / h
tanFov = tan( fov_y * 0.5 );
p[0][0] = 2*n/(r-l) = 1.0 / (tanFov * aspect)
p[1][1] = 2*n/(t-b) = 1.0 / tanFov
将屏幕(鼠标)坐标转换为标准化设备坐标:
vec2 MousePicker::getNormalizedCoords(double x, double y) {
GLint w, h;
glfwGetWindowSize(win, &width, &height);
GLfloat ndc_x = 2.0 * x/w - 1.0;
GLfloat ndc_y = 1.0 - 2.0 * y/h; // invert Y axis
mouseInfo.normalizedCoords = vec2(ndc_x, ndc_x);
return vec2(ndc_x, ndc_x);
}
计算从摄影机位置到世界空间中屏幕上某个点(鼠标位置)的光线:
view = view matrix
glm::mat4 invView = glm::inverse( view );
glm::vec3 P0 = invView * glm::vec3(0.0f, 0.0f, 0.0f);
// = glm::vec3( view[3][0], view[3][1], view[3][2] );
glm::vec3 dir = glm::normalize( invView * ray_P - P0 );
vec3 MousePicker::calculateMouseRay( void ) {
glfwGetCursorPos(win, &mouseInfo.xPos, &mouseInfo.yPos);
vec2 normalizedCoords = getNormalizedCoords(mouseInfo.xPos, mouseInfo.yPos);
ray_Px = normalizedCoords.x / projection[0][0]; // projection[0][0] == 1.0 / (tanFov * aspect)
ray_Py = normalizedCoords.y / projection[1][1]; // projection[1][1] == 1.0 / tanFov
glm::vec3 ray_P = vec3( ray_Px, ray_Py, -1.0f ) );
vec3 camPos = cam->getCameraPos(); // == glm::vec3( view[3][0], view[3][1], view[3][2] );
glm::mat4 invView = glm::inverse( view );
glm::vec3 P0 = camPos;
glm::vec3 dir = glm::normalize( invView * ray_P - P0 );
return dir;
}