I'm attempting to model a simple graphics pipeline - using Matlab at the moment as a modelling tool to get the transformations correct. I appreciate there are software tools that would make this easier - but I'm looking to understand the maths behind it and hence am looking to mostly use simple functions & matrices that I'm learning from a book (very retro!).I've successfully got through the stages of defining simple objects and converting them into a universal world space - but have come unstuck with the mathematics required to convert an object into view space and the back face culling.I believe my view space transformation is correct because when I plot the composite vectors they appear correct - but - when I do the back-face culling, it seems to fail to remove the correct triangles. Given that it depends only on two things, the line-of-sight vector and face normals, I can't work out what I'm doing wrong.When defining the triangles in local definition space, I did it so that all the normals pointed outwards. I've put my results into the image belowMy questions are:Have I gone wrong, or are my expectations incorrect?If so where, and how do I fix?ProgressI've plotted the normal's of all the shapes when in view space. They have all been inverted and now point inwards. Is this a property of the transformation and could it be responsible - or should this have no effect because all the polygons are affected the same?(Have changed the code to show this)clc; clear all; close all;%============Initial verticies & Faces of the shape===========[s1_vtx,s1_fcs] = Pyramid();[s2_vtx,s2_fcs] = Cube();%==============Transform Shape 1 ======================Tx = 0; Ty = 0; Tz = 0; %Translation vectors for x,y,z axesSx = 2; Sy = 2; Sz = 2;%Scaling factors in x,y,z dimensionsRx = pi/2; Ry = pi/4; Rz = pi/4; %Rotating factors in x,y,z dimensionstransform = scale(Sx,Sy,Sz)*rotate(Rx,0,0)*translate(Tx,Ty,Tz); %Merge transforms togethers1_vtx = transform*vertcat(s1_vtx,(ones(1,length(s1_vtx)))); %Add row of ones to end for multiplications1_vtx = s1_vtx(1:3,:); %And remove afterwards%==============Transform Shape 2 ======================Tx = 0.5; Ty = 0; Tz = 0.5;transform = scale(1,2,1)*translate(Tx,Ty,Tz);s2_vtx = transform*vertcat(s2_vtx,(ones(1,length(s2_vtx))));s2_vtx = s2_vtx(1:3,:);%======Create World Space ===========ws_vtx = horzcat(s1_vtx(1:3,:), s2_vtx(1:3,:)); %remove homogenous column for patchingws_fcs = horzcat(s1_fcs,(s2_fcs+(length(s1_vtx))));%======Plot World Space ===========grid on; hold on;scatter3(ws_vtx(1,:),ws_vtx(2,:),ws_vtx(3,:)) %Plot all the pointspatch('Faces',ws_fcs','Vertices',ws_vtx','Facecolor', 'none');for i = 1:length(ws_vtx)    str = sprintf('%d',i);    text(ws_vtx(1,i),ws_vtx(2,i),ws_vtx(3,i), str,'FontSize',16, 'Color','r', 'FontWeight','b');endpoints = zeros(3,3); %Contains 1 trianglefor i = 1:length(ws_fcs); %For each triangle    points(:,1:3) = ws_vtx(:,ws_fcs(1:3,i));    U = points(:,2) - points(:,1); %Get two non-parallel vectors    V = points(:,3) - points(:,1);    average = [0,0,0];    for j = 1:length(points)        average(j) = (points(j,1) + points(j,2) + points(j,3))/3;    end    N = cross(U,V)/norm(cross(U,V)); %Normal, normalised to mag 1    scatter3(average(1),average(2),average(3));    plot3([average(1), average(1)+N(1)],[average(2), average(2)+N(2)],[average(3), average(3)+N(3)]);end%==================Create view matrix===================focus = [1.5,0,1.5]; %The point we're looking atCx = 3; Cy = -3; Cz = 3; %Position of cameraVspec = [0;0;1]; %Specified up directionT = viewMat(focus, [Cx,Cy,Cz],Vspec); %Create viewspace transform matrixp = norm(focus - [Cx,Cy,Cz]);U = T(1,1:3); V = T(2,1:3); N = T(3,1:3); %New Up, Right & View direction vectors%============Plot the camera vectors=================scatter3(Cx,Cy,Cz,'s') %Plot the camera positionplot3([Cx, Cx+p*N(1)],[Cy, Cy+p*N(2)],[Cz, Cz+p*N(3)]);plot3([Cx, Cx+V(1)],[Cy, Cy+V(2)],[Cz, Cz+V(3)]);plot3([Cx, Cx+U(1)],[Cy, Cy+U(2)],[Cz, Cz+U(3)]);%==================Transform into View Space===================ws_vtx = T*vertcat(ws_vtx,(ones(1, length(ws_vtx)))); %Transform matrixws_vtx = ws_vtx(1:3,:); %Remove homogenous dimensionorigin = T*[Cx;Cy;Cz;1]; %Transform originCx = origin(1); Cy = origin(2); Cz = origin(3); %remove homogenous dimensionfocus = (T*horzcat(focus,1)')';%Transform focus pointfocus = focus(:,1:3);%remove homogenous dimension%==================Plot View Space=================figure(); hold on; grid on;patch('Faces',ws_fcs','Vertices',ws_vtx','Facecolor', 'none');scatter3(Cx, Cy, Cz, 's');scatter3(focus(1), focus(2), focus(3), 's');plot3([Cx, focus(1)],[Cy, focus(2)],[Cz,focus(3)], 'g');for i = 1:length(ws_vtx)    str = sprintf('%d',i);    text(ws_vtx(1,i),ws_vtx(2,i),ws_vtx(3,i), str,'FontSize',16, 'Color','r', 'FontWeight','b');end%================Plot normals of world space==============for i = 1:length(ws_fcs); %For each triangle    points(:,1:3) = ws_vtx(:,ws_fcs(1:3,i));    U = points(:,2) - points(:,1); %Get two non-parallel vectors    V = points(:,3) - points(:,1);    average = [0,0,0];    for j = 1:length(points)        average(j) = (points(j,1) + points(j,2) + points(j,3))/3;    end    N = cross(U,V)/norm(cross(U,V)); %Normal, normalised to mag 1    scatter3(average(1),average(2),average(3));    plot3([average(1), average(1)+N(1)],[average(2), average(2)+N(2)],[average(3), average(3)+N(3)]);end
Graphics Pipeline: Viewspace & Back face culling incorrectly
transformations;3d;matrices;vectors
I (believe) I've solved this (even if it has taken 2 days). My problem was essentially I wanted to take the dot product of the face normal, and line-of-sight vector like belowAnd determine the angle to see if the face was looking towards or away from the view point.My erroneous step was that I was doing this AFTER transforming from world-space to view-space - hence the line-of-sight vector I was using was no longer valid.Hence, to solve this, I simply performed the back-face culling in world-space, prior to the world-view space transformation!I've included functioning code showing the back face culling, but not the view-space transform.clear; clc; close all;%======Create World Space (hard-coded values for demo) ===========ws_vtx = [0,2,0,2,1,0.5,1.5,0.5,1.5,0.5,1.5,0.5,1.5;    0,0,0,0,-2,0,0,2,2,0,0,2,2;    0,0,2,2,1,0.5,0.5,0.5,0.5,1.5,1.5,1.5,1.5];ws_fcs = [1,2,4,3,3,1,6,6,6,6,7,7,9,8,8,8,10,10;    2,4,3,1,4,4,9,8,7,11,9,13,8,12,10,6,11,13;    5,5,5,5,1,2,7,9,11,10,13,11,13,13,12,10,13,12];%==================Create view matrix===================focus = [1.5,0,1.5]; %The point we're looking atCx = 3; Cy = -3; Cz = 3; %Position of cameraVspec = [0;0;1]; %Specified up directionT = viewMat(focus, [Cx,Cy,Cz],Vspec); %Create viewspace transform matrixp = norm(focus - [Cx,Cy,Cz]);U = T(1,1:3); V = T(2,1:3); N = T(3,1:3); %New Up, Right & View direction vectors%============Plot the camera vectors=================grid on; hold on; scatter3(Cx,Cy,Cz,'s'); %Plot the camera positionplot3([Cx, Cx+p*N(1)],[Cy, Cy+p*N(2)],[Cz, Cz+p*N(3)],'g');plot3([Cx, Cx+V(1)],[Cy, Cy+V(2)],[Cz, Cz+V(3)],'g');plot3([Cx, Cx+U(1)],[Cy, Cy+U(2)],[Cz, Cz+U(3)],'g');%===========Get Face Normals============================norm_fcs = zeros(3,length(ws_fcs));     for i = 1:length(ws_fcs); %For each triangle        points = zeros(3,3); %Contains 1 triangle        points(:,1:3) = ws_vtx(:,ws_fcs(1:3,i)); %Get points for triangle        U = points(:,2) - points(:,1); %Get two non-parallel vectors        V = points(:,3) - points(:,1);        norm_fcs(:,i) = cross(U,V); %Normal, normalised to mag 1    end%=================Back Face Culling======================null_vals = 0;for i = 1:length(ws_fcs) %Take each triangle & calculate normal    if dot(norm_fcs(:,i), N) > 0 %Dot product line of sight & normal of faces        ws_fcs(:,i) = [0;0;0]; %If > 0, not visible & remove        null_vals = null_vals + 1; %And increment counter    endendws_fcs_cat = zeros(3, length(ws_fcs) - null_vals); %Create new arraynull_vals = 0;for i = 1:length(ws_fcs)    if norm(ws_fcs(:,i)) == 0        null_vals = null_vals + 1;    else        ws_fcs_cat(:,i - null_vals) = ws_fcs(:,i);    endendws_fcs = ws_fcs_cat;%======Plot World Space ===========scatter3(ws_vtx(1,:),ws_vtx(2,:),ws_vtx(3,:)) %Plot all the pointspatch('Faces',ws_fcs','Vertices',ws_vtx','Facecolor', 'r', 'FaceAlpha', 0.5);for i = 1:length(ws_vtx)    str = sprintf('%d',i);    text(ws_vtx(1,i),ws_vtx(2,i),ws_vtx(3,i), str,'FontSize',16, 'Color','r', 'FontWeight','b');endpoints = zeros(3,3); %Contains 1 trianglefor i = 1:length(ws_fcs); %For each triangle    points(:,1:3) = ws_vtx(:,ws_fcs(1:3,i));    U = points(:,2) - points(:,1); %Get two non-parallel vectors    V = points(:,3) - points(:,1);    average = [0,0,0];    for j = 1:length(points)        average(j) = (points(j,1) + points(j,2) + points(j,3))/3;    end    N = cross(U,V)/norm(cross(U,V)); %Normal, normalised to mag 1    scatter3(average(1),average(2),average(3));    plot3([average(1), average(1)+N(1)],[average(2), average(2)+N(2)],[average(3), average(3)+N(3)]);endxlabel('x'); ylabel('y'); zlabel('z');