Практика формирования видеоряда напрямую из 3D программ, созданных на Unity/Unreal/OpenSceneGraph для создания 3D-обучающих видеофильмов показала высокую эффективность такого подхода. Качество синтезируемой модели часто практически не уступает по фотореалистичности видео, созданному “классически”, т.е. традиционным рендерингом из программ 3dMax/Maya/Cinema и т.д.
При этом имеется возможность управления камерой в режиме реального времени, используя стандартные средства ввода-вывода / клавиатура-мышь. Для выполнения простых пролетов камеры, например по орбите с равномерным приближением или отдалением такой подход является достаточным и не требует каких либо дополнительных устройств управления. Однако при сложных движениях камеры требуется контролировать большее количество параметров движения и как следствие использование стандартных средств ввода-вывода становится или неудобным или недостаточным. VR также не всегда применим, особенно при больших размерах объекта и/или при быстрых перемещениях камеры.
На основе нашего опыта создания видеоряда из 3D программ было принято решение создать пульт управления виртуальной камерой, для эффективного управления движениями камеры, управлением масштабов времени и реализацией управления сценарием происходящего в 3D.
В качестве базы было выбрано оборудование, имеющееся “в запасе”, а именно:
Arduino Due — плата микроконтроллера на базе процессора Atmel SAM3X8E ARM Cortex-M3 (описание). Это первая плата Arduino на основе 32-битного микроконтроллера с ARM ядром. На ней имеется 54 цифровых вход/выхода (из них 12 можно задействовать под выходы ШИМ), 12 аналоговых входов, 4 UARTа (аппаратных последовательных порта), a генератор тактовой частоты 84 МГц, связь по USB с поддержкой OTG, 2 ЦАП (цифро-аналоговых преобразователя), 2 TWI, разъем питания, разъем SPI, разъем JTAG. Частота процессора (CPU) 84 МГц. 96 КБ ОЗУ. 512 КБ флеш-памяти для хранения программ. контроллер DMA, который разгружает центральный процессор от выполнения интенсивных операций с памятью.
Да, немного перебор, но она была под рукой.
TFT-дисплей на базе ILI9341 с тачскрином (320*240 65к цветов)
Энкодер с кнопкой, переменные резисторы, два модуля джойстиков, переключатели и кнопки с фиксацией и без фксации.
Фото устройства в процессе монтажа в корпусе.
Код написан в Arduino IDE, с использованием библиотек:
include “SPI.h”
include “Adafruit_GFX.h”
include “Adafruit_ILI9341.h”
include “Joystick.h”
Код программы приведен в конце статьи, а пока результат:
Внутренности после монтажа (клавиши еще не смонтированы)
Вот так операционная система определяет устройство (можно поменять конечно)
Ну и собственно тестирование.
Исходный код программы:
#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"
#include "Joystick.h"
#include <EncButton2.h>
Joystick_ Joystick;
/*
Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID,
JOYSTICK_TYPE_MULTI_AXIS, 32, 0,
true, true, false, false, false, false,
true, true, false, false, false);
*/
const int analogInPin1 = A0;
const int analogInPin2 = A1;
const int analogInPin3 = A2;
const int analogInPin4 = A3;
const int analogInPin5 = A4;
const int analogInPin6 = A5;
const int analogInPin7 = A6;
// For the Adafruit shield, these are the default.
#define TFT_RST 8
#define TFT_DC 9
#define TFT_CS 10
#define TFT_MISO 50
#define TFT_MOSI 51
#define TFT_SCK 52
// Use hardware SPI (on Uno, #13, #12, #11) and the above for CS/DC
//Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);
// If using the breakout, change pins as desired
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCK, TFT_RST, TFT_MISO);
EncButton2<EB_ENC> enc(INPUT, 45, 43); // просто энкодер
// the setup function runs once when you press reset or power the board
void setup() {
//Serial.begin(9600);
// Set Range Values
Joystick.setXAxisRange(-127, 127);
Joystick.setYAxisRange(-127, 127);
Joystick.setZAxisRange(0, 255);
Joystick.setRxAxisRange(255, 0);
Joystick.setRyAxisRange(0, 255);
Joystick.setRzAxisRange(255, 0);
Joystick.setThrottleRange(0, 255);
Joystick.setRudderRange(0, 255);
Joystick.setAcceleratorRange(0, 255);
Joystick.setBrakeRange(0, 255);
Joystick.setSteeringRange(0, 255);
Joystick.begin(false);
pinMode(A0, INPUT_PULLUP);
pinMode(A1, INPUT_PULLUP);
pinMode(A2, INPUT_PULLUP);
pinMode(A3, INPUT_PULLUP);
pinMode(A4, INPUT_PULLUP);
pinMode(A5, INPUT_PULLUP);
pinMode(A6, INPUT_PULLUP);
pinMode(A7, INPUT_PULLUP);
pinMode(1, INPUT_PULLUP);
pinMode(2, INPUT_PULLUP);
pinMode(3, INPUT_PULLUP);
pinMode(4, INPUT_PULLUP);
pinMode(5, INPUT_PULLUP);
//pinMode(13, OUTPUT);
pinMode(LED_BUILTIN, OUTPUT);
pinMode(47, INPUT); //butt0 encoder
pinMode(45, INPUT); //encoder1
pinMode(43, INPUT); //encoder2
pinMode(38, INPUT); //butt1
pinMode(40, INPUT); //butt2
tft.begin();
// read diagnostics (optional but can help debug problems)
uint8_t x = tft.readcommand8(ILI9341_RDMODE);
//Serial.print("Display Power Mode: 0x"); Serial.println(x, HEX);
x = tft.readcommand8(ILI9341_RDMADCTL);
//Serial.print("MADCTL Mode: 0x"); Serial.println(x, HEX);
x = tft.readcommand8(ILI9341_RDPIXFMT);
// Serial.print("Pixel Format: 0x"); Serial.println(x, HEX);
x = tft.readcommand8(ILI9341_RDIMGFMT);
// Serial.print("Image Format: 0x"); Serial.println(x, HEX);
x = tft.readcommand8(ILI9341_RDSELFDIAG);
// Serial.print("Self Diagnostic: 0x"); Serial.println(x, HEX);
tft.setRotation(0); //0 1 2 3
tft.fillScreen(ILI9341_BLACK);
//unsigned long start = micros();
tft.setCursor(0, 0);
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(2);
tft.println("Lcontent.ru");
tft.setTextColor(ILI9341_BLUE);
tft.println("Joy module v.1.0");
tft.setTextColor(ILI9341_RED);
tft.println("by Maxim Gammer");
}
int OLD_sensorValue1 = 0;
int encoderValue=0;
// the loop function runs over and over again forever
void loop()
{
int sensorValue1 = 0;
int outputValue1 = 0;
int sensorValue2 = 0;
int outputValue2 = 0;
int sensorValue3 = 0;
int outputValue3 = 0;
int sensorValue4 = 0;
int outputValue4 = 0;
int sensorValue5 = 0;
int outputValue5 = 0;
int sensorValue6 = 0;
int outputValue6 = 0;
int sensorValue7 = 0;
int outputValue7 = 0;
sensorValue1 = analogRead(analogInPin1);
sensorValue2 = analogRead(analogInPin2);
sensorValue3 = analogRead(analogInPin3);
sensorValue4 = analogRead(analogInPin4);
sensorValue5 = analogRead(analogInPin5);
sensorValue6 = analogRead(analogInPin6);
sensorValue7 = analogRead(analogInPin7);
// map it to the range of the analog out:
outputValue1 = map(sensorValue1, 0, 1023, 0, 255);
outputValue2 = map(sensorValue2, 0, 1023, 0, 255);
outputValue3 = map(sensorValue3, 0, 1023, 0, 255);
outputValue4 = map(sensorValue4, 0, 1023, 0, 255);
outputValue5 = map(sensorValue5, 0, 1023, 0, 255);
outputValue6 = map(sensorValue6, 0, 1023, 0, 255);
outputValue7 = map(sensorValue7, 0, 1023, 0, 255);
Joystick.setYAxis(outputValue1 - 128);
Joystick.setXAxis(outputValue2 - 128);
Joystick.setRxAxis(outputValue3);
Joystick.setRyAxis(outputValue4);
Joystick.setZAxis(outputValue5);
Joystick.setRzAxis(outputValue6);
Joystick.setThrottle(outputValue7);
//Joystick.setRudder(255);
Joystick.setRudder(255);
Joystick.setAccelerator(255);
Joystick.setBrake(255);
Joystick.setSteering(255);
int buttonState0 = digitalRead(47);
Joystick.setButton(0, buttonState0);
int buttonState1 = digitalRead(38);
Joystick.setButton(1, buttonState1);
int buttonState2 = digitalRead(40);
Joystick.setButton(2, buttonState2);
enc.tick(); // опрос происходит здесь
if (enc.left())
{
encoderValue = encoderValue+45;
}
if (enc.right())
{
encoderValue = encoderValue-45;
}
if (encoderValue<=-45)
{
encoderValue=315;
}
else if (encoderValue>=360)
{
encoderValue=0;
}
int hatSwitch =0;
Joystick.setHatSwitch(hatSwitch, -1);
Joystick.setHatSwitch(hatSwitch, encoderValue); //0 45 90 135 180 225 270 315
Joystick.sendState();
//Serial.print("sensor = ");
//Serial.println(sensorValue);
/*
char TX[20];
//tft.fillRect(0, 0, 40, 10, ILI9341_BLACK); // textbgcolor is protected in Adafruit_GFX.h
tft.setTextSize(1);
tft.setCursor(0, 0);
tft.setTextColor(ILI9341_BLACK);
sprintf(TX,"%4d", OLD_sensorValue1);
tft.println(TX);
OLD_sensorValue1 = sensorValue1;
tft.setCursor(0, 0);
tft.setTextColor(ILI9341_WHITE);
sprintf(TX,"%4d", sensorValue1);
tft.println(TX); //"Hello World!"
delay(2);
*/
//digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
//delay(1000); // wait for a second
//digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
//delay(1000); // wait for a second
}