基于CMU图形库的3D方块渲染问题求助:无需额外导入,将面中心点转化为可渲染多边形并修正视角移动异常
Hey there! Let's break down your Minecraft-like block rendering issues step by step—you're already halfway there with the face center points, so let's turn those into solid polygons and fix the wonky movement/camera behavior.
1. Turning Face Centers into Polygon Faces
Right now you're rendering the center of each visible face, but to draw the full square face, you need to calculate all four vertices of the face, convert each to screen coordinates, then pass those points to the Polygon class. Here's how to approach it:
Step 1: Create a Reusable 3D-to-Screen Conversion Function
Your current code repeats the same angle math 6 times—let's fix that with a helper function that handles rotation and perspective for any 3D point. This will also fix the "flat" orthogonal look by adding proper perspective projection (so closer blocks appear larger):
import math def world_to_screen(vertex_x, vertex_y, vertex_z, player): # Calculate offset from the player's position dx = vertex_x - player.x dy = vertex_y - player.y dz = vertex_z - player.z # Convert player's yaw/pitch to radians yaw_rad = math.radians(player.yaw) pitch_rad = math.radians(player.pitch) # Rotate for yaw (around Y-axis) rotated_x = dx * math.cos(yaw_rad) + dz * math.sin(yaw_rad) rotated_z = -dx * math.sin(yaw_rad) + dz * math.cos(yaw_rad) # Rotate for pitch (around X-axis) camera_x = rotated_x camera_y = dy * math.cos(pitch_rad) - rotated_z * math.sin(pitch_rad) camera_z = dy * math.sin(pitch_rad) + rotated_z * math.cos(pitch_rad) # Skip points behind the player if camera_z <= 0: return None # Perspective projection (adjust focal_length to tweak zoom) focal_length = 200 screen_center_x = 200 screen_center_y = 600 # Matches your existing code's Y origin screen_x = screen_center_x + (camera_x / camera_z) * focal_length screen_y = screen_center_y - (camera_y / camera_z) * focal_length # Flip Y to match screen axis return (screen_x, screen_y)
Step 2: Render Full Faces with Polygon
For each visible face (like front, top, etc.), define its four world-space vertices, convert each to screen coordinates, then pass the list to Polygon. Here's an example for the front face:
def render(self, target): # Define your block's world position (replace with your actual block coords) block_x, block_y, block_z = self.x, self.y, self.z if self.front: # Front face vertices (square around the center point) front_vertices = [ (block_x - 0.5, block_y - 0.5, block_z + 0.5), (block_x + 0.5, block_y - 0.5, block_z + 0.5), (block_x + 0.5, block_y + 0.5, block_z + 0.5), (block_x - 0.5, block_y + 0.5, block_z + 0.5) ] # Convert all vertices to screen points screen_points = [] for v in front_vertices: sp = world_to_screen(v[0], v[1], v[2], target) if sp: screen_points.append(sp) # Render the polygon if all points are valid if len(screen_points) == 4: renderGroup.add(Polygon(screen_points)) # Optional: Keep rendering the center point for debugging center_sp = world_to_screen(block_x, block_y, block_z + 0.5, target) if center_sp: renderGroup.add(Circle(center_sp[0], center_sp[1], 2)) # Repeat this pattern for top/left/bottom/right/back faces with their respective vertices # Example for top face vertices: # top_vertices = [ # (block_x - 0.5, block_y + 0.5, block_z - 0.5), # (block_x + 0.5, block_y + 0.5, block_z - 0.5), # (block_x + 0.5, block_y + 0.5, block_z + 0.5), # (block_x - 0.5, block_y + 0.5, block_z + 0.5) # ]
2. Fixing Camera/Movement Anomalies
Your weird point movement and 360° camera glitches come from two main issues: improper angle wrapping and unconstrained pitch rotation. Here's how to fix them:
Step 1: Properly Wrap Angles
Instead of manually checking if yaw > 360, use modulo arithmetic to keep angles in the 0-360 range (this handles negative angles too):
# When updating yaw (e.g., left/right arrow keys) target.yaw = target.yaw % 360
Step 2: Constrain Pitch to Avoid Flip
Players shouldn't be able to look straight up/down beyond 90° (it causes perspective flip). Add limits when updating pitch:
# When updating pitch (e.g., up/down arrow keys) target.pitch += 2 # Adjust speed as needed if target.pitch > 90: target.pitch = 90 elif target.pitch < -90: target.pitch = -90
Step 3: Validate Movement Direction
Make sure WASD movement follows the player's yaw (facing direction), not world coordinates. For example:
# Example W (forward) movement move_speed = 0.1 yaw_rad = math.radians(target.yaw) target.x += math.cos(yaw_rad) * move_speed target.z -= math.sin(yaw_rad) * move_speed # Adjust sign based on your coordinate system # Example A (left) movement target.x -= math.sin(yaw_rad) * move_speed target.z -= math.cos(yaw_rad) * move_speed
Test this by printing your player's x/z coordinates when moving—you should see movement align with your facing direction.
内容的提问来源于stack exchange,提问作者guywhocode

