The aim of the below code using ray_cast function (thanks to @lemon) is to find all the visible vertices present in the camera view (previous post's Link). It does find some of the vertices of a mesh in the camera view, however, some of the vertices are missed as shown in Figure 1 and 2. But, for my work, I need all the vertices as shown in figure 3 and 4.
import bpy
from mathutils import Vector
from mathutils.bvhtree import BVHTree
from bpy_extras.object_utils import world_to_camera_view
# Create a BVH tree and return bvh and vertices in world coordinates
def BVHTreeAndVerticesInWorldFromObj( obj ):
mWorld = obj.matrix_world
vertsInWorld = [mWorld * v.co for v in obj.data.vertices]
bvh = BVHTree.FromPolygons( vertsInWorld, [p.vertices for p in obj.data.polygons] )
return bvh, vertsInWorld
# Deselect mesh polygons and vertices
def DeselectEdgesAndPolygons( obj ):
for p in obj.data.polygons:
p.select = False
for e in obj.data.edges:
e.select = False
# Get context elements: scene, camera and mesh
scene = bpy.context.scene
cam = bpy.data.objects['Camera']
obj = bpy.data.objects['Cube']
# Threshold to test if ray cast corresponds to the original vertex
limit = 0.0001
# Deselect mesh elements
DeselectEdgesAndPolygons( obj )
# In world coordinates, get a bvh tree and vertices
bvh, vertices = BVHTreeAndVerticesInWorldFromObj( obj )
print( '-------------------' )
for i, v in enumerate( vertices ):
# Get the 2D projection of the vertex
co2D = world_to_camera_view( scene, cam, v )
# By default, deselect it
obj.data.vertices[i].select = False
# If inside the camera view
if 0.0 <= co2D.x <= 1.0 and 0.0 <= co2D.y <= 1.0:
# Try a ray cast, in order to test the vertex visibility from the camera
location, normal, index, distance = bvh.ray_cast( cam.location, (v - cam.location).normalized() )
# If the ray hits something and if this hit is close to the vertex, we assume this is the vertex
if location and (v - location).length < limit:
obj.data.vertices[i].select = True
del bvh
I've modified the above code a bit just to test the behaviour of normal scene.ray_cast function (not the bvh.ray_cast). However, the results are same.
import bpy
from mathutils import Vector
from bpy_extras.object_utils import world_to_camera_view
# Deselect mesh polygons and vertices
def DeselectEdgesAndPolygons( obj ):
for p in obj.data.polygons:
p.select = False
for e in obj.data.edges:
e.select = False
# Get context elements: scene, camera and mesh
scene = bpy.context.scene
cam = bpy.data.objects['Camera']
obj = bpy.data.objects['Cube']
# Threshold to test if ray cast corresponds to the original vertex
limit = 0.0001
# Deselect mesh elements
DeselectEdgesAndPolygons( obj )
# In world coordinates, get vertices
mWorld = obj.matrix_world
vertices = [mWorld * v.co for v in obj.data.vertices]
print( '-------------------' )
for i, v in enumerate( vertices ):
# Get the 2D projection of the vertex
co2D = world_to_camera_view( scene, cam, v )
# By default, deselect it
obj.data.vertices[i].select = False
# If inside the camera view
if 0.0 <= co2D.x <= 1.0 and 0.0 <= co2D.y <= 1.0:
# Try a ray cast, in order to test the vertex visibility from the camera
ray= obj.ray_cast( cam.location, (v - cam.location).normalized() )
# If the ray hits something and if this hit is close to the vertex, we assume this is the vertex
if ray[0] and (v - ray[1]).length < limit:
obj.data.vertices[i].select = True
Hence, I'm wondering whether there is some error with the ray_cast functions or something I'm doing wrong.