筆者介紹:姜雪偉,IT公司技術(shù)合伙人,IT高級(jí)講師,CSDN社區(qū)專家,特邀編輯,暢銷書作者,國(guó)家專利發(fā)明人;已出版書籍:《手把手教你架構(gòu)3D游戲引擎》電子工業(yè)出版社和《Unity3D實(shí)戰(zhàn)核心技術(shù)詳解》電子工業(yè)出版社等。
射線檢測(cè)在游戲中使用的非常廣泛,我們利用射線開(kāi)發(fā)時(shí),開(kāi)發(fā)者只是調(diào)用引擎的接口便可實(shí)現(xiàn),但是我們要了解其內(nèi)部實(shí)現(xiàn)原理,這樣我們也能夠自己封裝射線檢測(cè)算法,射線檢測(cè)算法利用程度非常高,不論是使用鼠標(biāo)還是觸摸屏,都需要用到射線檢測(cè),比如在虛擬仿真中,導(dǎo)彈追蹤物體時(shí),它需要通過(guò)從導(dǎo)彈上發(fā)出的射線1直朝向物體,角色尋路也能夠通過(guò)在身體發(fā)出的射線檢測(cè)與物體本身的碰撞盒接觸后,用于查看物體是不是產(chǎn)生了碰撞。射線檢測(cè)還用于對(duì)物體身上的材質(zhì)處理,比如墻體的遮擋關(guān)系,使用攝像機(jī)發(fā)出的射線方向指向角色,如果它們之間的射線檢測(cè)到物體比如墻體,可以編寫邏輯改變角色的材質(zhì)進(jìn)行透明處理,效果以下圖所示:
固然我們也能夠使用對(duì)墻體進(jìn)行透明設(shè)置,這也是1種處理方式,是將墻體進(jìn)行透明設(shè)置效果以下圖所示:
其在程序中表現(xiàn)的效果以下所示:
從相機(jī)發(fā)出兩條射線,射線與墻體的碰撞體產(chǎn)生接觸,程序會(huì)改變墻體的Shader,替換其材質(zhì),讓其透明。以下是Unity3D引擎使用案例,核心代碼以下所示:
if (Physics.Linecast(pos, transform.position, out hit)) { last_obj = hit.collider.gameObject; int length = last_obj.transform.childCount; string name_tag = last_obj.tag; if (name_tag == "wall") { Material mat = new Material(Shader.Find("Transparent/Diffuse")); curr_obj = last_obj; for (int i = 0; i < length; i++) { Transform child = last_obj.transform.GetChild(i); child.GetComponent<Renderer>().material = mat; mat.mainTexture = (Texture)Resources.Load("Atlas/test_pub_tm"); } } }
下面給開(kāi)發(fā)者介紹1下,射線碰撞的原理,把1個(gè)物體放到場(chǎng)景中做了1個(gè)局部坐標(biāo)到世界坐標(biāo)變換,效果以下圖所示:
將物體投影后,假定紅的表示X軸,綠的表示Y軸,中間表示物體的碰撞盒,1條射線穿過(guò)后效果以下所示:
射線穿過(guò)我們要計(jì)算其是不是與碰撞盒產(chǎn)生碰撞,主要是通過(guò)射線與碰撞盒相交值的大小進(jìn)行判斷,效果以下圖所示:
射線與碰撞體產(chǎn)生碰撞后的判定效果圖以下所示:
根據(jù)以上的判斷我們可以寫下代碼,對(duì)3D物體,我們要分別判斷X,Y,Z3個(gè)軸。核心代碼以下所示:
bool TestRayOBBIntersection( glm::vec3 ray_origin, // Ray origin, in world space glm::vec3 ray_direction, // Ray direction (NOT target position!), in world space. Must be normalize()'d. glm::vec3 aabb_min, // Minimum X,Y,Z coords of the mesh when not transformed at all. glm::vec3 aabb_max, // Maximum X,Y,Z coords. Often aabb_min*⑴ if your mesh is centered, but it's not always the case. glm::mat4 ModelMatrix, // Transformation applied to the mesh (which will thus be also applied to its bounding box) float& intersection_distance // Output : distance between ray_origin and the intersection with the OBB ){ // Intersection method from Real-Time Rendering and Essential Mathematics for Games float tMin = 0.0f; float tMax = 100000.0f; glm::vec3 OBBposition_worldspace(ModelMatrix[3].x, ModelMatrix[3].y, ModelMatrix[3].z); glm::vec3 delta = OBBposition_worldspace - ray_origin; // Test intersection with the 2 planes perpendicular to the OBB's X axis { glm::vec3 xaxis(ModelMatrix[0].x, ModelMatrix[0].y, ModelMatrix[0].z); float e = glm::dot(xaxis, delta); float f = glm::dot(ray_direction, xaxis); if ( fabs(f) > 0.001f ){ // Standard case float t1 = (e+aabb_min.x)/f; // Intersection with the "left" plane float t2 = (e+aabb_max.x)/f; // Intersection with the "right" plane // t1 and t2 now contain distances betwen ray origin and ray-plane intersections // We want t1 to represent the nearest intersection, // so if it's not the case, invert t1 and t2 if (t1>t2){ float w=t1;t1=t2;t2=w; // swap t1 and t2 } // tMax is the nearest "far" intersection (amongst the X,Y and Z planes pairs) if ( t2 < tMax ) tMax = t2; // tMin is the farthest "near" intersection (amongst the X,Y and Z planes pairs) if ( t1 > tMin ) tMin = t1; // And here's the trick : // If "far" is closer than "near", then there is NO intersection. // See the images in the tutorials for the visual explanation. if (tMax < tMin ) return false; }else{ // Rare case : the ray is almost parallel to the planes, so they don't have any "intersection" if(-e+aabb_min.x > 0.0f || -e+aabb_max.x < 0.0f) return false; } } // Test intersection with the 2 planes perpendicular to the OBB's Y axis // Exactly the same thing than above. { glm::vec3 yaxis(ModelMatrix[1].x, ModelMatrix[1].y, ModelMatrix[1].z); float e = glm::dot(yaxis, delta); float f = glm::dot(ray_direction, yaxis); if ( fabs(f) > 0.001f ){ float t1 = (e+aabb_min.y)/f; float t2 = (e+aabb_max.y)/f; if (t1>t2){float w=t1;t1=t2;t2=w;} if ( t2 < tMax ) tMax = t2; if ( t1 > tMin ) tMin = t1; if (tMin > tMax) return false; }else{ if(-e+aabb_min.y > 0.0f || -e+aabb_max.y < 0.0f) return false; } } // Test intersection with the 2 planes perpendicular to the OBB's Z axis // Exactly the same thing than above. { glm::vec3 zaxis(ModelMatrix[2].x, ModelMatrix[2].y, ModelMatrix[2].z); float e = glm::dot(zaxis, delta); float f = glm::dot(ray_direction, zaxis); if ( fabs(f) > 0.001f ){ float t1 = (e+aabb_min.z)/f; float t2 = (e+aabb_max.z)/f; if (t1>t2){float w=t1;t1=t2;t2=w;} if ( t2 < tMax ) tMax = t2; if ( t1 > tMin ) tMin = t1; if (tMin > tMax) return false; }else{ if(-e+aabb_min.z > 0.0f || -e+aabb_max.z < 0.0f) return false; } } intersection_distance = tMin; return true; }
上一篇 springMVC源碼--Controller控制器
下一篇 react-native報(bào)錯(cuò)解決方法 in next release empty section headers will be rendered