頂点に何らかの重みを割り当て、サーモグラフィー的に表示したい。三角形一枚の色はデフォルトでは各頂点の線形補間になってしまうので、フラグメントシェーダで重みを色に変換する関数を通して着色する。なおこのコードは以前作ったRust+OpenGLで三角形を表示するコードのdraw.rsを書き換えている。
use std::ptr::null; use std::ffi::CString; use std::ffi::CStr;
pub fn draw_prepare() ->(gl::types::GLuint, gl::types::GLuint){ // 頂点の定義 let vertices: [f32;9]=[ 0.0, 0.5, 0.0, // 上頂点 -0.5, -0.5, 0.0, // 左下 0.5, -0.5, 0.0, // 右下 ]; // スカラー値 let scalar:[f32;3]=[ 1.0, 0.3, 0.0, ]; let mut vertexbuffer=0; unsafe{ gl::GenBuffers(1,&mut vertexbuffer); gl::BindBuffer(gl::ARRAY_BUFFER,vertexbuffer); gl::BufferData( gl::ARRAY_BUFFER, 3*3*std::mem::size_of::<gl::types::GLfloat>() as gl::types::GLsizeiptr, vertices.as_ptr() as *const _, gl::STATIC_DRAW ); } let mut scalarbuffer=0; unsafe{ gl::GenBuffers(1,&mut scalarbuffer); gl::BindBuffer(gl::ARRAY_BUFFER,scalarbuffer); gl::BufferData( gl::ARRAY_BUFFER, 3*std::mem::size_of::<gl::types::GLfloat>() as gl::types::GLsizeiptr, scalar.as_ptr() as *const _, gl::STATIC_DRAW ); } (vertexbuffer,scalarbuffer) }
pub fn prepare_vertex_shader()->gl::types::GLuint{ let mut VertexShaderID = 0; // 頂点シェーダプログラム let vertex_shader_source = "\ #version 460 core layout (location = 0) in vec3 aPos;
// layout (location = 1) in vec3 incolor; layout (location = 1) in float scalar; // out vec4 vertexColor; out float fragscalar; uniform mat4 modelViewMatrix; uniform mat4 projectionMatrix; void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4(aPos, 1.0);
// vertexColor = vec4(incolor, 1.0); fragscalar = scalar; } "; let c_src = CString::new(vertex_shader_source).unwrap(); let mut Result:gl::types::GLint = 0; let mut InfoLogLength:i32 = 0; let mut info_log; unsafe{ VertexShaderID = gl::CreateShader(gl::VERTEX_SHADER); gl::ShaderSource( VertexShaderID, 1, &c_src.as_ptr(), std::ptr::null() ); gl::CompileShader(VertexShaderID); // シェーダのチェック gl::GetShaderiv(VertexShaderID,gl::COMPILE_STATUS,&mut Result); gl::GetShaderiv(VertexShaderID,gl::INFO_LOG_LENGTH,&mut InfoLogLength); if InfoLogLength > 0 { info_log = vec![0u8; InfoLogLength as usize]; if Result == gl::FALSE as i32 { gl::GetShaderInfoLog( VertexShaderID, InfoLogLength, std::ptr::null_mut(), info_log.as_mut_ptr() as *mut gl::types::GLchar ); if let Ok(msg) = CStr::from_ptr(info_log.as_ptr() as *const i8).to_str() { println!("Vertex Shader Error :\n {}\n", msg); } } } } VertexShaderID }
pub fn prepare_fragment_shader()->gl::types::GLuint{ let mut FragmentShaderID=0; let fragment_shader_source = "\ #version 460 core out vec4 FragColor; // in vec4 vertexColor; in float fragscalar; // スカラーをサーモグラフィーのRGBに変換 vec3 scalarToThermalColor(float scalar) { // 0.0〜1.0の範囲に制限 scalar = clamp(scalar, 0.0, 1.0); // サーモグラフィーカラーの計算 vec3 color; if (scalar < 0.25) { color = vec3(0.0, 4.0 * scalar, 1.0); // 青 → シアン } else if (scalar < 0.5) { color = vec3(0.0, 1.0, 1.0 - 4.0 * (scalar - 0.25)); // シアン → 緑 } else if (scalar < 0.75) { color = vec3(4.0 * (scalar - 0.5), 1.0, 0.0); // 緑 → 黄 } else { color = vec3(1.0, 1.0 - 4.0 * (scalar - 0.75), 0.0); // 黄 → 赤 } return color; } void main() { // FragColor = vertexColor; FragColor = vec4(scalarToThermalColor(fragscalar), 1.0); // 出力 } "; let c_str = CString::new(fragment_shader_source).unwrap(); let mut Result:gl::types::GLint=0; let mut InfoLogLength:i32=0; let mut info_log; unsafe { FragmentShaderID = gl::CreateShader(gl::FRAGMENT_SHADER); gl::ShaderSource( FragmentShaderID, 1, &c_str.as_ptr(), null() ); gl::CompileShader(FragmentShaderID); // フラグメントシェーダ gl::GetShaderiv(FragmentShaderID, gl::COMPILE_STATUS, &mut Result); gl::GetShaderiv(FragmentShaderID, gl::INFO_LOG_LENGTH, &mut InfoLogLength); if InfoLogLength > 0 { if Result == gl::FALSE as i32 { info_log = vec![0u8; InfoLogLength as usize]; gl::GetShaderInfoLog( FragmentShaderID, InfoLogLength, std::ptr::null_mut(), info_log.as_mut_ptr() as *mut gl::types::GLchar ); if let Ok(msg) = CStr::from_ptr(info_log.as_ptr() as *const i8).to_str() { println!("Fragment Shader Error :\n{}\n", msg); } } } } FragmentShaderID }
pub fn link_program(VertexShaderID:gl::types::GLuint,FragmentShaderID:gl::types::GLuint)->gl::types::GLuint{ let mut Result:gl::types::GLint = gl::FALSE as i32; let mut InfoLogLength:i32=0; let mut ProgramID:gl::types::GLuint=0; println!("Linking program"); unsafe{ ProgramID = gl::CreateProgram(); gl::AttachShader(ProgramID,VertexShaderID); gl::AttachShader(ProgramID,FragmentShaderID); gl::LinkProgram(ProgramID); gl::GetProgramiv(ProgramID,gl::LINK_STATUS,&mut Result); gl::GetProgramiv(ProgramID,gl::INFO_LOG_LENGTH,&mut InfoLogLength); if InfoLogLength > 0 { let mut ProgramErrorMessage = vec![0u8; InfoLogLength as usize]; gl::GetProgramInfoLog( ProgramID, InfoLogLength, std::ptr::null_mut(), ProgramErrorMessage.as_mut_ptr() as *mut gl::types::GLchar ); if let Ok(msg) = CStr::from_ptr(ProgramErrorMessage.as_ptr() as *const i8).to_str() { println!("Program Link Error:\n{}\n",msg); } } } ProgramID }
pub fn draw_triangle(programid:gl::types::GLuint,vertexbuffer: gl::types::GLuint,scalarbuffer:gl::types::GLuint){ unsafe { gl::UseProgram(programid); } let proj_mat:[f32;16]=[ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, ]; let model_mat:[f32;16]=[ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, ]; let proj; let model; unsafe { let proj_location_name = CString::new("projectionMatrix").unwrap(); let model_location_name = CString::new("modelViewMatrix").unwrap(); proj = gl::GetUniformLocation(programid,proj_location_name.as_ptr()); model = gl::GetUniformLocation(programid,model_location_name.as_ptr()); gl::UniformMatrix4fv(proj,1,gl::FALSE,proj_mat.as_ptr()); gl::UniformMatrix4fv(model,1,gl::FALSE,model_mat.as_ptr()); } /* // デバッグ println!("proj location: {}",proj); println!("model location: {}",model); */ unsafe{ gl::EnableVertexAttribArray(0); gl::BindBuffer(gl::ARRAY_BUFFER,vertexbuffer); gl::VertexAttribPointer( 0, 3, gl::FLOAT, gl::FALSE, 0, null() ); } unsafe{ gl::EnableVertexAttribArray(1); gl::BindBuffer(gl::ARRAY_BUFFER,scalarbuffer); gl::VertexAttribPointer( 1, 1, // 一次元配列 gl::FLOAT, gl::FALSE, 0, null() ); } unsafe { gl::DrawArrays(gl::TRIANGLES, 0,3); let err_check = gl::GetError(); if err_check != gl::NO_ERROR { println!("ERROR::: {}\n", err_check); } gl::DisableVertexAttribArray(0); gl::DisableVertexAttribArray(1); } unsafe { gl::UseProgram(0); } }
vtkPolyDataにAddArrayし、表示時にSetNameで指定したスカラー名でSelectColorArrayを呼び出す。かならずModifiedも呼び出す。
#include <iostream> //VTK_MODULE_INITに必要 #include <vtkAutoInit.h> #include <vtkSmartPointer.h> #include <vtkRenderer.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> #include <vtkPolyDataMapper.h> #include <vtkActor.h> #include <vtkPointData.h> #include <vtkRenderWindowInteractor.h> #include <vtkCommand.h> #include <vtkInteractorStyleTrackballCamera.h> #pragma comment(lib,"opengl32.lib") #pragma comment(lib,"psapi.lib") #pragma comment(lib,"dbghelp.lib") #pragma comment(lib,"ws2_32.lib") //必須 VTK_MODULE_INIT(vtkRenderingOpenGL2); VTK_MODULE_INIT(vtkInteractionStyle);
// キーボード入力を処理 class MyCommand : public vtkCommand { bool IsRed = true; public: static MyCommand* New() { return new MyCommand; } void SetMapper(vtkSmartPointer<vtkPolyDataMapper> mapper) { this->Mapper = mapper; } void Execute(vtkObject* caller, unsigned long eventId, void*) override { if (eventId == vtkCommand::KeyPressEvent) { vtkRenderWindowInteractor* interactor = dynamic_cast<vtkRenderWindowInteractor*>(caller); std::string key = interactor->GetKeySym(); // 注意 デフォルト機能で E キーで終了してしまう
if(IsRed) { Mapper->SelectColorArray("ColorsRed");// 頂点色切替 Mapper->Modified(); } else { Mapper->SelectColorArray("ColorsBlue"); Mapper->Modified(); }
IsRed = !IsRed; interactor->GetRenderWindow()->Render(); } } private: vtkSmartPointer<vtkPolyDataMapper> Mapper; };
////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////
// ランダムな点群を生成 vtkSmartPointer<vtkPolyData> CreateRandomCloud() { vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New(); vtkSmartPointer<vtkCellArray> vertices = vtkSmartPointer<vtkCellArray>::New(); vtkSmartPointer<vtkPolyData> polyData = vtkSmartPointer<vtkPolyData>::New(); polyData->SetPoints(points); polyData->SetVerts(vertices); vtkSmartPointer<vtkCellArray> verts = vtkSmartPointer<vtkCellArray>::New(); vtkIdType pid[1]; for (int i = 0; i < 10000; i++) { double p[3]; p[0] = vtkMath::Random(-1.0, 1.0); p[1] = vtkMath::Random(-1.0, 1.0); p[2] = vtkMath::Random(-1.0, 1.0); pid[0] = points->InsertNextPoint(p); verts->InsertNextCell(1, pid); } polyData->SetVerts(verts); return polyData; }
// データに色情報を追加 // 呼び出すたびにdataに色情報が追加される void SetColor(vtkSmartPointer<vtkPolyData> data,const char* scalarname,std::array<unsigned char,3> rgb) { size_t PointNum = data->GetNumberOfPoints(); vtkSmartPointer<vtkUnsignedCharArray> colorsNew = vtkSmartPointer<vtkUnsignedCharArray>::New(); colorsNew->SetNumberOfComponents(3); colorsNew->SetName(scalarname); for (int i = 0; i < PointNum; i++) { colorsNew->InsertNextTuple3(rgb[0], rgb[1], rgb[2]); } data->GetPointData()->AddArray(colorsNew); }
int main(int /*argc*/, char** /*argv*/) { vtkSmartPointer<vtkPolyData> cloud = CreateRandomCloud(); SetColor(cloud, "ColorsRed",{255,0,0}); SetColor(cloud, "ColorsBlue", { 0,0,255 }); // cloudのactorを作成 vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New(); mapper->SetInputData(cloud); vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New(); actor->SetMapper(mapper); ////////////////////////////////////// ////////////////////////////////////// ////////////////////////////////////// // 使用する色をAddArrayで指定したスカラー値を使用するように設定 mapper->SetScalarVisibility(1); // スカラー値を使用して色をつける設定 mapper->SetScalarModeToUsePointFieldData(); // スカラー値はポイントデータに設定されているものを使用 mapper->SetColorModeToDefault(); // カラーモードをデフォルトに設定 // 実際にどのスカラー値を使用するかを設定 // 切り替え時にはかならずModified()を呼ぶ mapper->SelectColorArray("ColorsRed"); mapper->Modified();// マッパーを更新 ////////////////////////////////////// ////////////////////////////////////// ////////////////////////////////////// ////////////////////////////////////// auto renderer = vtkSmartPointer<vtkRenderer>::New(); renderer->AddActor(actor); renderer->ResetCamera(); ////////////////////////////////////// auto interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New(); ////////////////////////////////////// // キー入力で色を切り替えるコマンドを設定 vtkSmartPointer<MyCommand> colorToggleCommand = vtkSmartPointer<MyCommand>::New(); colorToggleCommand->SetMapper(mapper); interactor->AddObserver(vtkCommand::KeyPressEvent, colorToggleCommand); ////////////////////////////////////// auto renderWindow = vtkSmartPointer<vtkRenderWindow>::New(); renderWindow->AddRenderer(renderer); renderWindow->SetInteractor(interactor); renderWindow->Render(); interactor->Start(); //イベントループへ入る return 0; }
SVGはXMLで書かれる画像フォーマットなので、pugixmlで出力できる。
以前pugixmlを導入したので、それを使う。
#include "pugixml.hpp" int main() { pugi::xml_document doc; pugi::xml_node mysvg = doc.append_child("svg"); mysvg.append_attribute("xmlns") = "http://www.w3.org/2000/svg"; mysvg.append_attribute("width") = "200"; mysvg.append_attribute("height") = "200"; // 背景を塗りつぶすための矩形 pugi::xml_node bgrect = mysvg.append_child("rect"); bgrect.append_attribute("x") = "0"; bgrect.append_attribute("y") = "0"; bgrect.append_attribute("width") = "200"; bgrect.append_attribute("height") = "200"; bgrect.append_attribute("fill") = "rgb(200,200,200)"; // 円 pugi::xml_node circle = mysvg.append_child("circle"); circle.append_attribute("cx") = "50"; circle.append_attribute("cy") = "50"; circle.append_attribute("r") = "40"; circle.append_attribute("fill") = "red"; // 線 pugi::xml_node line = mysvg.append_child("line"); line.append_attribute("x1") = "10"; line.append_attribute("y1") = "10"; line.append_attribute("x2") = "150"; line.append_attribute("y2") = "80"; line.append_attribute("stroke") = "black"; line.append_attribute("stroke-width") = "4"; // 矩形 pugi::xml_node rect = mysvg.append_child("rect"); rect.append_attribute("x") = "50"; rect.append_attribute("y") = "50"; rect.append_attribute("width") = "80"; rect.append_attribute("height") = "80"; rect.append_attribute("fill") = "blue"; rect.append_attribute("stroke") = "green"; rect.append_attribute("stroke-width") = "6"; // ファイルに保存 doc.save_file("test.svg"); }
imagemagickで画像に変換できる